-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
More typescript fixes #8903
More typescript fixes #8903
Conversation
Thanks for the pull request @thw0rted!
Reviewers, don't forget to make sure that:
|
I just noticed that these typings break my build, not because of any changes I made, but because I rebased them on top of I think in the big PR from last week there was some discussion about why these classes don't use inheritance, but I didn't see any mention of the |
I'm not sure what you mean by this. How is it actually broken in master? |
This is fixed via new smokescreen tests in #8908. If a new ImageryProvider is added, we'll need to add it to the test, but that is not a common occurrence and should just be part of the review. |
We're not quite ready to do it yet (since there are lots of places, not just ImageryProvider) where we would want to do this; but it is up for discussion. I'm going to start an issue on it soon. |
Source/Core/Event.js
Outdated
@@ -7,6 +7,7 @@ import defined from "./defined.js"; | |||
* exposed as a property for others to subscribe to. | |||
* | |||
* @alias Event | |||
* @template Listener extends Function = Function |
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 can't fine @template
in the official JSDoc documentation, https://jsdoc.app/.
It this not actually valid JSDoc and instead a Closure compiler extension? (as mentioned here https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler#template-t)
Also, the extends Function = Function
thing working almost looks like a happy accident.
I'm still learning my way around TS generics, but does this basically let non-templated versions of Even still work with a default Function callback that takes any number/type of arguments?
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.
@kring meant to CC you here because I'd like to hear your thoughts on this as well, since there are lots of places we'll want to make use of generics if we can.
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.
Ideally the Event class's definition would look like this:
export class Event<Listener extends (...args: any[]) => any = (...args: any[]) => any> {
constructor();
readonly numberOfListeners: number;
addEventListener(listener: Listener, scope?: any): Event.RemoveCallback;
removeEventListener(listener: Listener, scope?: any): boolean;
raiseEvent(...arguments: Parameters<Listener>): void;
}
Using (...args: any[]) => any
is better than Function
because it actually has a call signature, unlike Function
. So you can't use Function
with Parameters<Listener>
to get the nicely-typed signature for raiseEvent
.
I just pushed up a change to this, and it seems to work ok. Even the generated reference doc looks pretty good, even if we're abusing it a bit. But let me know if I've missed something.
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're right, @template
is a Closure extension -- look at the very bottom of the tsd-jsdoc
README. That's also the bit where it said you need the plugin to make it work. There are also a bunch of different competing syntaxes for function signatures: Function.<number, boolean>
, function(number):boolean
, (n: number) => boolean
. I'm not sure which of those is "normal" JSDoc, which is comes from Closure, etc. I know Function
(without constraints) is fine for everybody.
I believe in my experience that the TS compiler treats Function
and (...args: any[]) => any
as equivalent, sort of like any
vs {[key: any]: any}
, though there may be some nuance I'm not familiar with. A more specific call signature, like (n: number, b: boolean) => void
should be assignable to either. Also, these are event listeners, so I believe the return type should always be void
-- there's no way to do anything with the function result, right?
I actually originally wanted to make Event generic on <...args: any[]>
, which would make typing raiseEvent
easier, but generic rest parameters are a relatively recent Typescript addition and I can't even begin to imagine how you'd document them as valid JSDoc. Full disclosure: I wasn't too worried about raiseEvent
because I didn't expect users to be raising events, just consuming them.
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.
Function isn't quite the same in that you can't use it with Parameters<T>
. This TypeScript playground shows what I mean:
Also note that nice static typing on raiseEvent
with my new version, even though you're right that most CesiumJS users won't be raising events. The last two lines in the TS playground are intentionally errors to show that the calls are type checked correctly.
It all seems to work ok with tsd-jsdoc and with jsdoc itself, unless you spot a problem I didn't.
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.
Also, these are event listeners, so I believe the return type should always be void
Yeah that's a good point. Certainly raiseEvent ignores the return values and returns void itself. No major harm in the listeners returning non-void, but it's a little misleading.
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.
there may be some nuance I'm not familiar with
You found it! 😁
Seriously though, this is great, as long as we don't wind up with some kind of issue with other consumers of the docs -- like I said, I'm not really clear on who wants function
vs Function
vs Function.<T,U,V>
vs () => Whatever
and I was worried that the arrow-notation version was "too Typescript-y". If the team is happy with it, I certainly am too.
Source/Core/Resource.js
Outdated
* Initialization options for the Resource constructor | ||
* | ||
* @property {String} url The url of the resource. | ||
* @property {Resource.QueryParameters} [queryParameters] An object containing query parameters that will be sent when retrieving the resource. |
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.
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 think maybe the previous branch had updated some of the other references, but I lost track of it when these changes were backed out? Anyway, it's been a minute, but I think this #6205 (comment) might be the reason why, at least on my own typings, I wanted a more constrained type. I thought that issue ({x: [1,2,3]}
turning into x=1&x=2&x=3
instead of x=1,2,3
) had actually been resolved, which would mean that the values are not simply toString
'd. Could be mistaken? As a later comment in that thread points out, Resource
isn't really "for" consumers anyway.
(What really should be getting passed here is URLSearchParams but I get that you still have legacy support to consider and it's almost certainly not worth the weight of a polyfill.)
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 to be sure, I did some testing and:
var resource = new Resource({
url: "http://test.invalid/test",
queryParameters: {
key1: ["value1", "value5", "value6"],
key2: "value2",
}
});
Gets turned into http://test.invalid/test?key1=value1&key1=value5&key1=value6&key2=value2
And
var someObject = { toString: () => "bob" };
var resource = new Resource({
url: "http://test.invalid/test",
queryParameters: {
key1: someObject,
},
});
Gets turned into: http://test.invalid/test?key1=bob
So toString is definitely called, though it's smart enough to loop over arrays and add the key multiple times. While I appreciate the desire to more clearly define the types, I think having a marker object in the JSDoc HTML is confusing. We would also need to update the other places where queryPamaters is used, such as setQueryParamaters
. I'd rather just back the QueryParameters type altogether and use object. We can certainly keep the Resource options object you introduced.
If you feel strongly about this, please open a separate issue specifically to discuss QueryParameters, I don't want to see this specific piece of code hold up the rest of the PR.
Thanks!
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 think I originally created that type in response to the issue I linked above, more as a hint to myself than anything. Please go ahead and back it out and if I feel the need to place some constraint for myself, I will.
The key word in the line about Re: Listener showing up as an unreferenced type, would it be clearer if it were simply renamed to something more specific like "EventCallback" or something, and maybe there was a note describing it in the class description? Alternately, does the AST from the JSDoc parser include a node for generic type parameters that maybe needs to be added to the doc template? If not, that sounds like an issue to file with jsdoc. |
Sorry if I'm confused here, but I just don't get why this is needed at all. From my understanding as it's documented the result can either be |
You can document function lookup(key: "strThing"): string;
function lookup(key: "numThing"): number;
function lookup(key: string): string | number | Array { ... }
lookup("strThing"); // types as string
lookup("numThing"); // types as number
lookup("otherThing"); // can't tell at compile time, types as string | number | Array
Basically, it makes the docs a bit bigger but it saves users from having to cast the result. I think this is preferable but I understand it has some drawbacks. (You should see the declaration for |
@thw0rted can you please merge in master, this will bring in the TS smokescreen changes and some other cleanup. Thanks! |
I just rebased and am running Everything seems fine except for my PropertyBag hacks. As mentioned in the OP, the consensus seems to be that supporting arbitrary properties (index signature) on a class is an antipattern and it's tough to describe properly in TS. Either you make an In my typings, as in this PR, I opted for the latter, because I don't expect end users to make a Anyway, I pushed one more commit that "fixes" the smoke test by not actually instantiating a PropertyBag, and instead doing a cast to make TS think an assignment is happening. If you're happy with this, great, and if not we can back out all the PropertyBag related stuff. |
dont know if I'm too late to the party. Found that constructing the |
@thw0rted regarding Perhaps the best design would be to have a separate |
Also please mate don't force push. You accidentally clobbered some of my changes. I'll re-add them. |
Specs/TypeScript/index.ts
Outdated
@@ -121,7 +121,7 @@ property = new ConstantProperty(1); | |||
property = new TimeIntervalCollectionProperty(); | |||
property = new CompositeProperty(); | |||
property = new SampledProperty(Cartesian3); | |||
property = new PropertyBag(); | |||
property = {} as PropertyBag; |
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.
Do we want this change? I understand it's hard to prevent this assignment via the type system, but it's not going to work at runtime because expected properties like propertyNames
will be missing.
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.
Oh I see, PropertyBag
can't be constructed anymore because it's a type
. Pretty sure that's a problem. It may not be common to construct PropertyBag
, but it happens. TerriaJS does it.
Hi @bkuster,
I don't quite understand why the whitespace would throw off the typings. The generated Cesium.d.ts looks fine to me, and certainly TS itself isn't sensitive to whitespace there. I'm not opposed to that change just for the sake of tidiness, but it could be worth double-checking there isn't something else going on there. |
@kring yeah, must have been some freaky thing over my end, all fine now (probably forgot to rebuild the TS after pulling from master or similar), sorry for the noise. |
Kevin, sorry about clobbering your changes -- I'm used to working in my own "private" branches where rebase-and-force is a safe workflow. I need some practice working with a shared upstream! You're right that the docs are pretty unwieldy for Re: PropertyBag, I'm going to do a bit more research and see if there's not some way to make a declaration that's a) new-able, b) has strongly-typed methods and explicit properties, and c) also has a strongly typed index signature. I suspect there isn't, so we'll wind up having to give the index signature an any-type. ETA: if anybody wants to follow microsoft/TypeScript#38977, feel free, but I'm not holding my breath. |
No worries! :)
Yeah that'd be cool. Again, though, I'm not really sure it's worth the cycles. Even in the best-case scenario it still forces to maintain duplicated doc, for very minimal benefit. |
Thanks again for your contribution @thw0rted! No one has commented on this pull request in 30 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 30 days. If you'd like me to stop, just comment with |
2 similar comments
Thanks again for your contribution @thw0rted! No one has commented on this pull request in 30 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 30 days. If you'd like me to stop, just comment with |
Thanks again for your contribution @thw0rted! No one has commented on this pull request in 30 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 30 days. If you'd like me to stop, just comment with |
5d50f4e
to
f0bdedf
Compare
Sorry to be away from this for so long. I just rebased back to master (I did check here first to make sure I wouldn't clobber anybody!) and am going to see if I can rework this, possibly into multiple PRs. |
@@ -5,6 +5,8 @@ import Resource from "./Resource.js"; | |||
|
|||
/*global CESIUM_BASE_URL*/ | |||
|
|||
// TODO: all these should have JSDoc tags, or mark as private |
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 think there were only one or two of these functions that the compiler actually noticed and complained about, but I figured while we're in there it would make sense to just mark everything.
Improved EntityCluster description
Add a note about warning in buildModuleUrl
4888e61
to
67e27a7
Compare
Thanks again for your contribution @thw0rted! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
1 similar comment
Thanks again for your contribution @thw0rted! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
681e511
to
f14fd3d
Compare
I just created a new PR, #10292, which has all the important changes from here without any of e.g. |
@thw0rted Can we close this in favor of #10292 (review)? Even closed, this PR will still exist for the sake of documenting discussion. |
Fine by me. I'll try to take a look at your comments over there today -- thanks for taking the time to look it over! |
Make
Event
generic and add a callback signature in a few placesThere are a lot more places this could be used but it should be non-breaking and just allows for future refinement.
ConstructorOptions
forResource
I can't remember why this one got taken out?
exportKml
overload actually worksThe catch is that I had to exclude it from
prettier
until they address prettier/prettier#7724. The overloads do show up separately in the docs but I think it looks OK that way.Another stab at making PropertyBag actually work
I pinged jsdoc/jsdoc#1285 for an update. All the SO questions, etc, that I can find about how to describe something like PropertyBag (a class with its own methods and properties, and also an index signature for "other" properties) say the only option is an intersection type, which JSDoc refuses to support on the basis that it's not part of Closure Compiler. (The questions actually all say "this is an antipattern, don't do this in the first place" but it's probably too late to change?)
I think the hack I came up with will work for now. Basically, the class is called
PropertyBag
so that it gets the right name duringgenerateDocumentation
, but for Typescript purposes it's renamed toPropertyBagBase
with a regex replace, then a top level type alias is declared using union types.The one thing I wasn't able to do is figure out how to document in JSDoc that
PropertyBag
has an index signature. Is there a more Javascript-y name for "index signature"? I think I would have called it the "index operator" back before I learned TS, but I can't find anything online using either name. This isn't a new problem -- the docs have never actually given an example of using e.g.ent.properties.someProperty
-- but since I was in there anyway I was looking to improve it. If you can find the right tag to use, by all means let's add it.