-
Notifications
You must be signed in to change notification settings - Fork 448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: add typedefs #802
chore: add typedefs #802
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enable us to generate typescript declaration files automatically on release with aegir.
Will we get these automatically now, do we need to update anything for the release?
What are we missing / What is next?
For what's missing, has this been checked against any downstream TS projects? The intellisense is nice, but if we're going to be generating the types on release we should make sure to test this on a larger existing project. For example, can we swap out the types here in Lodestar, https://github.com/ChainSafe/lodestar/tree/master/packages/lodestar, for the types at https://github.com/ChainSafe/libp2p-ts.git that it's using in dev?
src/peer-store/book.js
Outdated
@@ -30,7 +29,7 @@ class Book { | |||
/** | |||
* Map known peers to their data. | |||
* | |||
* @type {Map<string, Array<Data>} | |||
* @type {Map<string, Array<*>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of any, *
, should these be generics (T
)? Based on how the books are structured the data type should be consistent for each book.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can make Book
generic by adding @template T
above class definition. That would also make options.eventTransformer
bounded.
src/identify/index.js
Outdated
@@ -290,7 +264,7 @@ class IdentifyService { | |||
* | |||
* @private | |||
* @param {object} options | |||
* @param {*} options.stream | |||
* @param {DuplexIterable} options.stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably track adding a type for Stream
in the interfaces module, that should probably live with Connection. While any DuplexIterable
should be fine, we really want these to be Stream's
Pending ipfs/aegir#671 for support on release. But yes, I can do that test by that time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just added few nits & general feedback
*/ | ||
|
||
/** | ||
* @typedef {Object} AutoRelayProperties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: In js-ipfs we settled on calling injected deps with Config
suffix e.g. AutoRelayConfig`. Might be worth following same convention across both code bases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably mentioning in the one below, right?
The current approach is: xProperties
to libp2p references needed and xOptions
for user options, so that we can use xOptions
on the top level config docs. We can definitely change xOptions
to xConfig
and I would be happy in having a better name for xProperties
if you have any suggestions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bo I was referring to xProperties
actually, maybe I’ve misread the code, but it looked like dependencies injected into AutoRelay
and we called such things with xConfig
e.g.:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to get a good way for here. So, in the libp2p stand point we have lots of places where we inject libp2p, or some libp2p components on other libp2p components. Moreover, these libp2p components have configurations that users can modify.
We need to differentiate between them. My initial approach was to make ComponentNameProperties
for the libp2p components we provide, and ComponentNameOptions
for user options. Naming config for dependencies that are not "configurable" does not seem the best naming to me. However, I am not totally happy with xProperties
, so if we can find a better convention would be great
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe AutoRelayComponents
then ?
The reason I end up calling those config in js-ipfs is because it is configuration of the component. It is true that user can't modify those but who ever creates a component can provide alternative configuration e.g. in memory repo vs on disk repo etc...
Things that user can configure are referred as PrefixOptions
.
I don't have strong feeling about naming convention itself, just would love to settle on the same convention across ipfs and libp2p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Components seems a good alternative. Will wait for other people to weight in before renaming everywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think we should block this due to naming convention. Lets just have a separate discussion thread somewhere instead and we can always come back and rename things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I agree. But let's keep the discussion open until hugo/jacob comments
src/peer-store/book.js
Outdated
@@ -30,7 +29,7 @@ class Book { | |||
/** | |||
* Map known peers to their data. | |||
* | |||
* @type {Map<string, Array<Data>} | |||
* @type {Map<string, Array<*>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can make Book
generic by adding @template T
above class definition. That would also make options.eventTransformer
bounded.
26014cd
to
1c54160
Compare
The first batch of review comments were addressed. I will test this with lodestar over the week. Meanwhile, @Gozala created https://www.npmjs.com/package/typed-events.ts for the EventEmitter stuff ❤️ . |
627486d
to
28b9f85
Compare
a674e4a
to
149c19a
Compare
6a56dac
to
ded2a88
Compare
2b76df2
to
265bf48
Compare
74475c1
to
e519a6c
Compare
e519a6c
to
55910c8
Compare
@hugomrdias @Gozala @jacobheun This is ready and already tested successfully with lodestar build. Unfortunately, it seems that we will not be able to use templates in the PeerStore Book and I needed to add any to the data types for now (more info on: #821 -- I will create an issue in typescript as the aegir type check works ok with #821 templates, but then the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @vasco-santos this must have been a lot of work & I can’t wait for this to lang and propagate across code base 🤩
I added bunch of minor comments and called out few instances where use of generics seems more appropriate. I think it’s mostly there, I just would really like type-check action to get uncommented to ensure all of this type-checks.
*/ | ||
|
||
/** | ||
* @typedef {Object} AutoRelayProperties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think we should block this due to naming convention. Lets just have a separate discussion thread somewhere instead and we can always come back and rename things.
@@ -68,7 +71,7 @@ class Stats extends EventEmitter { | |||
/** | |||
* Returns a clone of the current stats. | |||
* | |||
* @returns {Map<string, Stat>} | |||
* @returns {Object} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @returns {Object} | |
* @returns {Record<string, Stat>} |
Assuming Stat
is defined somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stat is not defined yet. Metrics still have a lot of work to be done as follow up and as this is internal code I did not prfioritize getting through all this now.
class Book { | ||
/** | ||
* The Book is the skeleton for the PeerStore books. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like Book
here supposed to be generic with Data
type parameter e.g.
/**
* @template Data
*/
class Book {
set(peerId:PeerId, data:Data[]|Data):void
get(peerId:PeerID):Data[]|undefined
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it should probably have a composed template. I started doing it and it was working good here. However, when I tried to integrate this in lodestar, I was getting issues on building their project. You have more information on this in #802 (comment) . I have it done in #821 and I already talked with @hugomrdias about it. It seems like a typescript issue (I need to open an issue there), and we should track it before changing it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have looked into this & turns out this was different issue fixed in typescript@4.1.x. So, I have submitted #831 against #821, which addresses this problem and fixes few type miss matches that have came up.
I think it would be good to take a look at that pull as it also uncovered some issues that I have missed when reviewing this.
src/record/peer-record/index.js
Outdated
* @param {Record} other | ||
* @returns {boolean} | ||
* @param {PeerRecord} other | ||
* @returns {other is Record} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since other is already required to be a PeerRecord
instance which implements Record
interface it is granted so this return type will have no effect.
* @returns {other is Record} | |
* @returns {boolean} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It errors with:
Property 'equals' in type 'PeerRecord' is not assignable to the same property in base type 'Record'.
Type '(other: PeerRecord) => boolean' is not assignable to type '(other: any) => other is Record'.
Signature '(other: PeerRecord): boolean' must be a type predicate.
Per https://github.com/libp2p/js-libp2p-interfaces/blob/feat/add-types/src/record/types.ts#L20
Should I just change the param to any?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so there are couple of things here:
-
That error occurs because interface says you can pass anything to the method, but then implementation contradicts it by saying by saying you only pass
PeerRecord
, implying that you can't passnumber
while interface says you can do that. In other words you can accept wider range of inputs (superset types) in implementations than type / interface requires and be compatible, but you can not do other way around. -
I'm honestly confused by
equals(other: any): other is Record
. Usingother is Type
kind of annotations are only useful for predicates that are meant to be used for type refinements e.g. example illustrating thisinterface Duck { quack(): void } interface Dog { bark(): void } const isDuck = (value:any):value is Duck => value && typeof value.quack === 'function' export const workingExample = (value:Dog|Duck) => { if (isDuck(value)) { // In this block type of value is `Duck` value.quack() } if (value && typeof value.quack === "function") { // ^^^^^ Property 'quack' does not exist on type 'Duck | Dog'. value.quack() // ^^^^^ Property 'quack' does not exist on type 'Duck | Dog'. } }
Note that in some cases on might restrict type of value in predicate in some way (does not need to be
any
), but the point is that TS essentially treats such predicate functions to do type refinements.I am not sure why one would want to do type refinement with
equals
check e.g. because if type ofvalue
invalue.equals(other)
is known and it's equal toother
thanvalue
could be used in it's place e.g.:const example = (duck:Duck, other:Duck|Dog) { if (duck.equals(other)) { duck.quack() // as opposed to other.quack() } }
-
I think you may want to use
Record
for parameter type, as in it does not matter ifPeerRecord
implementation is used or some compatibleOtherRecord
implementation. -
I think generally it is best to avoid
any
unless you want to tell TS to just shut up about that value. That is because in that case TS will not complain no matter you do with it. I think in most cases actual intent is to say that value is something of any type (via genericsinterface Record { equals <T>(other:T):boolean }
or more simply that value is ofunknown
type (viainterface Record { equals(other:unknown):boolean }
). This inspired me to write (what I hope is fun) explainer
If I had to guess you either want equals(other:unknown): boolean
or bit more constrained equals(other:Record):boolean
in both interface and a class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the example and detailed go through. I am changing the interface + here to unknown and leverage the instance of
Co-authored-by: Irakli Gozalishvili <contact@gozala.io>
eb5b4dc
to
9b863bd
Compare
I think the issue you may be referring to is microsoft/TypeScript#41258 which got fixed just yesterday. But in the meantime there is also a workaround that projects can use: |
Thanks! I think it is that! ❤️ Sadly, they did not release it yet.
I feel it is better to add that as a follow up PR. Seems odd to me to release What do you think? Any concerns on this approach? From the user stand point, not having template specified in the book yet will not have influence as they will use the book implementations that export the correct types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think? Any concerns on this approach? From the user stand point, not having template specified in the book yet will not have influence as they will use the book implementations that export the correct types.
@@ -583,7 +645,9 @@ class Libp2p extends EventEmitter { | |||
|
|||
// Transport modules with discovery | |||
for (const Transport of this.transportManager.getTransports()) { | |||
// @ts-ignore Transport interface does not include discovery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitted a followup issue #829
@@ -1,3 +1,4 @@ | |||
// @ts-nocheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created #830 for that.
Co-authored-by: Irakli Gozalishvili <contact@gozala.io>
This PR adds typedefs in most of the core codebase. Libp2p interfaces was updated to leverage its new types.
Github actions replaced travis, which also enables us to just use it for windows 🎉
This brings a set of advantages:
Follow up work: #830
Needs:
At the moment, it is using a branch from the PR below that has a prepare script to generate the build while we do not release it.