Skip to content
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

Add TypeScript declarations. #4928

Merged
merged 21 commits into from
Dec 19, 2017
Merged

Add TypeScript declarations. #4928

merged 21 commits into from
Dec 19, 2017

Conversation

aomarks
Copy link
Member

@aomarks aomarks commented Nov 10, 2017

Generated by https://github.com/Polymer/gen-typescript-declarations. Regenerate with gulp generate-typescript. Types are output to a types/ directory.

Fixes #4025

Copy link
Member

@dfreedm dfreedm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move all the d.ts files to a types folder?

gulpfile.js Outdated
@@ -267,8 +267,18 @@ gulp.task('generate-externs', ['clean'], () => {
});
});

gulp.task('generate-typescript', () => {
let genTs = require('@polymer/gen-typescript-declarations').generateDeclarations;
del.sync(['**/*.d.ts']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use promise api with del

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(async/await ftw)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@justinfagnani
Copy link
Contributor

I think siblings makes sense, especially with the move to modules coming up.

/cc @FredKSchott and @usergenic because we'll need modulizer to re-generate the types.

@aomarks
Copy link
Member Author

aomarks commented Nov 17, 2017

@justinfagnani I'm fine with that, but note that once we're on modules, we'll also be on NPM, so we can and should add types as part of prepack instead of checking into source.

I think the objection to sibling files from @azakus is that it clutters up the source repo. Siblings for 2.0 is simpler from my perspective, but I understand not wanting to clutter the repo.

Can you elaborate on how this will work with modulizer? I would have thought we'd just blow away the 2.0 types before we run modulizer, and re-run the generator afterwards, once it has support for modules?

@TimvdLippe
Copy link
Contributor

I have updated the description to reference #4025 which this PR fixes as well.

Annotate that dedupingMixin returns T.
Includes hand written types for cases that couldn't be generated.

Uses https://github.com/Polymer/gen-typescript-declarations.

Run with `gulp generate-typescript`.

Also asyncify the gulpfile with fs-extra.
@aomarks
Copy link
Member Author

aomarks commented Dec 8, 2017

Hi all. This is ready for another look. Highly recommend reviewing the 3 commits separately.

  • Types are now output to a types/ directory instead of as sibling files. We made this decision to avoid cluttering the repo. For Polymer 3 on NPM, we will instead output as siblings as part of prepublish, instead of committing to the repo at all.

  • Integrates all of the fixes listed at https://github.com/Polymer/gen-typescript-declarations/issues/23. There are still a few missing APIs, but I don't think we should block merging on that. All of the missing APIs are things that Analyzer doesn't know about at all, so they have also never been in our documentation.

  • Added a commit with some minor JSDoc tweaks.

@aomarks
Copy link
Member Author

aomarks commented Dec 8, 2017

Also, this now includes the extra-types.d.ts file, which are TypeScript versions of the hand-written types we needed from the Closure externs.

Copy link
Member

@dfreedm dfreedm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a note that we now require Node 8 in the README?

Generally LGTM, minus that nit and need to double-check closure with return type on deduping mixin.

@@ -38,6 +38,7 @@
* @memberof Polymer
* @template T
* @param {T} mixin ES6 class expression mixin to wrap
* @return {T}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I remember, the magic voodoo that Closure Compiler uses for mixins doesn't like a return type here. Can you double check that gulp lint-closure doesn't produce any warnings?

Copy link
Member Author

@aomarks aomarks Dec 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a note that we now require Node 8 in the README?

Added some items to the CHANGELOG. Where should I mention the Node version in the README? It doesn't currently mention the gulp script. (async/await was actually 7.6 btw).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I remember, the magic voodoo that Closure Compiler uses for mixins doesn't like a return type here. Can you double check that gulp lint-closure doesn't produce any warnings?

I ran gulp lint, and it produces a lot of warnings. Nothing about dedupingMixin that I can see, though. Here's the log, does it look normal?: https://gist.github.com/aomarks/3adb586df000a36e682cc4f87137606b

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closure is compiling cleanly now.

Copy link
Contributor

@TimvdLippe TimvdLippe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a couple of questions regarding the return types of several methods.
Most notably, I see several methods that have void|null, where the code has no return statement and therefore should be just void.
Also there are several callbacks that have return type any, but the element definitions do not define a return type for these methods.
(Is there a reason these callbacks are typed anyways, as it seems they are only included if they are specified if the element uses them)

I am not sure if the overall goal is 100% type-correct, but as a TypeScript user I would expect the types to be as specific as possible.
Therefore if methods do not return anything, void is more specific than any.
Since all of the types are generated, I am not sure if this is a bug in the gen-typescript-declarations or that our Closure types are wrong.

To summarize the other points:

  1. Change void|null to void
  2. Change any to void for return types where functions do not return anything
  3. Change X|null to just X (for most of the cases)
  4. Unify all callback declarations to 1 class that everyone extends, probably just in property-accessors.d.ts, which is the lowest entry-point
  5. Some other small comments in relation to specific functions
  6. Remove |null when argument is already optional with X?

Sorry for the many comments, but I am really excited for this change! Just would like to be as specific as possible and not "lie" through our types 😂

*/
function ArraySelectorMixin<T extends new(...args: any[]) => {}>(base: T): {
new(...args: any[]): ArraySelectorMixin & Polymer.ElementMixin
} & T
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some nice TypeScript voodo 👌

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these would be easier to read if you define the Constructor<T> type.

/**
* Clears the selection state.
*/
clearSelection(): any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be void

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added @returns annotation.

*
* @param item Item from `items` array to deselect
*/
deselect(item: any): void|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should just be void. I do not see a return statement in this method that returns null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Fixed nullable void bug in Polymer/gen-typescript-declarations#40.

*/
attributeChangedCallback(): any;
connectedCallback(): any;
disconnectedCallback(): any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these methods are defined on HTMLElement which should be void as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added @returns annotation.

* let img = customElements.get('dom-module').import('foo', 'img');
*/
class DomModule extends HTMLElement {
attributeChangedCallback(name: any, old: any, value: any): any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name can be string here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added @param annotation.

/**
* Adds a `Polymer.Debouncer` to a list of globally flushable tasks.
*/
function enqueueDebouncer(debouncer: Polymer.Debouncer|null): void|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can not be null. That would throw an error later in flushDebouncers()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Updated @param annotation.

/**
* Adds an event listener to a node for the given gesture type.
*/
function addListener(node: Node|null, evType: string, handler: Function|null): boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these (and in the later methods) can not be |null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Updated @param annotations.

* In the `onload` callback, the `import` property of the `link`
* element will contain the imported document contents.
*/
function importHref(href: string, onload?: Function|null, onerror?: Function|null, optAsync?: boolean): HTMLLinkElement|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this type is different than the one specified in legacy-element-mixin.d.ts. Duplication FTW?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Made them the same.

/**
* Legacy settings.
*/
namespace settings {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add the declarations for the following:

namespace settings {
  useNativeCSSProperties: boolean;
  useNativeCustomElements: boolean;
  useShadow: boolean;
}

Copy link
Member Author

@aomarks aomarks Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the @namespace annotation from settings to Settings so at least that's correct now. But actually there is no way in Analyzer to model this object and its interface. Going to punt on the full interface for now. The main way people use this is with a <script> tag in their index.html anyway, so it's unlikely it would need to be referenced from TypeScript.

* To modify these typings, edit the source file(s):
* lib/utils/unresolved.html
*/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe do not generate the file if there were no types in it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we mirror the HTML import graph to the reference import graph, so that complicates that a bit, since dependents need to know whether their dependency has anything. See also https://github.com/Polymer/gen-typescript-declarations/blob/master/src/gen-ts.ts#L114

@aomarks
Copy link
Member Author

aomarks commented Dec 16, 2017

PTAL.

This PR is up to date with the latest changes to Polymer, Analyzer, and the type generator.

Copy link
Contributor

@justinfagnani justinfagnani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is basically ready to merge, so it has my approval. Any nits can be fixed up later, otherwise this could go around forever. I'd like to see it go out with a 2.4.0 prerelease to get feedback on the types if possible.

I left a handful of observations on generated types that I think could be better (like a lot of methods that return any but probably should return void), but I think those can be incrementally improved either in the generator or Polymer itself. Just add issues.

One thing that would be nice is if we could run the generated types through the type checker to make sure they're at least well formed and all referenced built-in types are defined.

gulpfile.js Outdated
const files = await genTs('.', config);
for (const [filePath, contents] of files) {
await fs.outputFile(path.join('types', filePath), contents);
}
});

gulp.task('update-version', () => {
gulp.src('lib/utils/boot.html')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi, this task looks like it's missing a return statement. It should return the stream pipeline.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't changed in this PR, but added a return.

* Types from "externs/closure-types.js"
*/

type Polymer_PropertyEffects = Polymer.PropertyEffects;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Add a comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer necessary. Fixed in Polymer/gen-typescript-declarations#47.


/// <reference path="lib/mixins/property-effects.d.ts" />

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a /* comment, since it's not a doc comment on the subsequent line?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

};

interface PolymerInit {
is: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any comments to copy here? TODO maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added TODO. Nothing in the externs to copy so I'd be writing something new.

}

/**
* Types from "lib/utils/async.html"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have types from regular source files? Is it cause we don't have interfaces in there? Might be worth a comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some interfaces are defined directly in the source if they are only used in that one place. Not sure I need to comment that here? Did end up removing one case, since AsyncModule turned out to be a re-definition of AsyncInterface.

*
* @returns Array of assigned nodes
*/
getDistributedNodes(): Array<Node|null>|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some places you have Foo[] and here Array<Foo>. TODO to parse Closure and convert?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deliberate. If it's an array of a named type, we do Foo[], if it's anything more complicated we do Array<Foo | Bar | Baz>, following the google typescript style guide.

* @param property Name of the property
* @param value Value to set
*/
_setProperty(property: string, value: any): any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should return void

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by Tim in #4998

*
* @returns Constructed HTMLTemplateElement
*/
function html(strings: ITemplateArray|null, ...values: any[]): HTMLTemplateElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null shouldn't be allowed here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (updated annotation in Polymer).

*
* @returns The link element for the URL to be loaded.
*/
function importHref(href: string, onload?: Function, onerror?: Function, optAsync?: boolean): HTMLLinkElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to have better types for the callbacks. Issue in Polymer core?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, would just need to update the @params in import-href.html. Punting for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny, I filed an issue a ways back and it got comments recently: #4603

/**
* Closure type equivalence for tagged template literal function argument.
*/
type ITemplateArray = TemplateStringsArray;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't see this when I looked at Polymer.html. Can't you just add this to the Closure -> TS type converter functions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimvdLippe
Copy link
Contributor

I have a couple more comments:

  1. It seems that all static methods are not typed. For example https://www.polymer-project.org/2.0/docs/api/elements/Polymer.Element#staticmethod-createPropertiesForAttributes is not found in the types.
  2. Running the tasks outputs some warnings, but I have no clue where they are coming from:
$ yarn run build generate-typescript
yarn run v1.3.2
$ gulp generate-typescript
[14:11:58] Using gulpfile ~/Projects/polymer/polymer/gulpfile.js
[14:11:58] Starting 'generate-typescript'...
Unknown syntax.
Unknown syntax.
Unknown syntax.
Could not find a name for class.
[14:12:04] Finished 'generate-typescript' after 5.85 s
Done in 6.75s.

I have opened #4998 to tighten almost all of the types to the correct value. This removes all : any, types methods that were completely missing types and fixes up some incorrect types.

@aomarks
Copy link
Member Author

aomarks commented Dec 17, 2017

I think these would be easier to read if you define the Constructor type.

I didn't want to pollute the global namespace with this type. I would have to somehow ensure it's only defined once when multiple repos' generated types are compiled together.

@aomarks
Copy link
Member Author

aomarks commented Dec 17, 2017

It seems that all static methods are not typed. For example https://www.polymer-project.org/2.0/docs/api/elements/Polymer.Element#staticmethod-createPropertiesForAttributes is not found in the types.

Oops, yeah quite an omission. I've added static method support in Polymer/gen-typescript-declarations#46. Turned out representing static methods for mixins required significant refactoring of the way mixins are emitted.

@aomarks
Copy link
Member Author

aomarks commented Dec 17, 2017

One thing that would be nice is if we could run the generated types through the type checker to make sure they're at least well formed and all referenced built-in types are defined

I've been testing manually, the current typings compile cleanly. It would be nice to automate it, maybe fail on Travis if it's not compiling. Maybe we should do the same for Closure compile too. Don't think I'll do that in this PR though.

@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this State. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@aomarks
Copy link
Member Author

aomarks commented Dec 19, 2017

Running the tasks outputs some warnings, but I have no clue where they are coming from:

I would like to improve the warnings output by integrating with the very nice Analyzer warnings printer. For another time though.

Unknown syntax.
Unknown syntax.
Unknown syntax.

These were from not supporting Object<foo, bar> syntax. Fixed in Polymer/gen-typescript-declarations#48.

Could not find a name for class.

This is from https://github.com/Polymer/polymer/blob/master/lib/utils/templatize.html#L61. Safe to ignore.

@aomarks
Copy link
Member Author

aomarks commented Dec 19, 2017

I think this is ready to merge. All comments addressed.

@justinfagnani
Copy link
Contributor

LGTM still

Copy link
Contributor

@TimvdLippe TimvdLippe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM for now. There are still some incorrect : any and incorrectly exposed private variables, but these are very few nits. I think it is better to merge this now and let end-users play with TypeScript support to get feedback, rather than perfect it right away. Nice work @aomarks !

@43081j
Copy link
Contributor

43081j commented Dec 19, 2017

Just a couple of things but they could be sorted in a future PR:

Annotations:

  • dom-repeat's indexForElement has an annotation of {*} for its return type, but should probably be number
  • properties-changed mixin should have createdProperties as void return type
  • properties-mixin should have finalize as void return type
  • async animationFrame.run should have function(number):void parameter
  • render-status beforeNextRender and afterNextRender parameters should be function():void

If you want to get this merged, i don't mind doing a PR for these annotations.

Misc:

  • does anyone know of progress or a PR to add connectedCallback and what not to typescript's shipped definitions? there's a few places we have to define these but they should really be inherited
  • animationFrame.run and animationFrame.cancel are duplicated

Great work @aomarks, will be testing this out more through the week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants