Type definitions

That’s great, thanks!

As I see there are some places (e.g. constructor’s) with type “any” although the type information is available:

  class Mark {
   constructor(type: any, attrs: any);
   type: MarkType;
   attrs: any;
   ...

How did you generate the file? From code comments? Maybe we can improve this a little before publishing to typing registry

I’m currently working on a project using ProseMirror with TypeScript. I’ve noticed that the bindings available through DefinitelyTyped are outdated (they describe version 0.18 of ProseMirror) and also inaccurate in some places.

Some of the inaccuracies can be fixed by improving the generator that takes the getdocs documentation and produces the type declarations:

  • API calls that may return null or undefined are marked with a ? in front of the return type (e.g. maybeChild(index: number) → ?Node) in the ProseMirror documentation. The TypeScript declarations currently lack this information (maybeChild(index: number): ProsemirrorNode). Using the null and undefined types introduced in TypeScript 2.0 we could give this function the more correct signature maybeChild(index: number): ProsemirrorNode | undefined | null. Looking at the implementation of maybeChild, we can see that maybeChild may return undefined, but does not return null. So the most accurate type would be maybeChild(index: number): ProsemirrorNode | undefined in this case. But this can’t be deduced from the getdocs annotations in their current form.
  • Types of the form Object<SomeType> that describe javascript objects whose values are of the type SomeType aren’t translated correctly. For example, the nodes member of the Schema class has the annotation nodes: Object; instead of the more accurate nodes: { [name: string]: NodeType };

Then there are some corrections that can only done manually:

  • The DOMOutputSpec interface described in prose in the documentation can be manually translated to a TypeScript definition:
   interface DOMOutputSpecArray {		    interface DOMOutputSpecArray {
      0: string,		      0: string,
      1?: DOMOutputSpec | 0 | { [attr: string]: string },		 +    1?: DOMOutputSpec | 0 | Object,
      2?: DOMOutputSpec | 0,		      2?: DOMOutputSpec | 0,
      3?: DOMOutputSpec | 0,		      3?: DOMOutputSpec | 0,
      4?: DOMOutputSpec | 0,		      4?: DOMOutputSpec | 0,
     5?: DOMOutputSpec | 0,		     5?: DOMOutputSpec | 0,
     6?: DOMOutputSpec | 0,		     6?: DOMOutputSpec | 0,
     7?: DOMOutputSpec | 0,		     7?: DOMOutputSpec | 0,
     8?: DOMOutputSpec | 0,		     8?: DOMOutputSpec | 0,
     9?: DOMOutputSpec | 0		     9?: DOMOutputSpec | 0
   }		   }
   type DOMOutputSpec		   type DOMOutputSpec
       = string		       = string
       | Node		       | Node
       | DOMOutputSpecArray		       | DOMOutputSpecArray
  • Many methods of Transform (e.g. clearMarkup(from: number, to: number) → Transform) return the object itself, so you can build transforms by chaining calls together. When you call these methods on the child class Transaction, you will obviously get a Transaction back. But the TypeScript signature is clearMarkup(from: number, to: number): Transform, so TypeScript thinks that you only get a transform back. Using polymorphic this types, a type introduced for chainable APIs, we can give clearMarkup the declaration clearMarkup(from: number, to: number): this. Then TypeScript will be able to infer that someTransaction.clearMarkup(from, to) is still a transaction, not only a transform.

I would like to help bringing the type declarations up to date and fixing these inaccuracies. The bindings currently published on DefinitelyTyped to to have been generated using [davidka/buildtypedefs (https://github.com/davidka/buildtypedefs). Is that correct, @davidka? (Are there any instructions on how to produce the type definitions using your tool somewhere?) I think that we should fix the first group of inaccuracies in the generator, rerun the translator on the current version of ProseMirror and then check the generated type declarations manually and fix/enhance inaccurate ones.

Maybe we can add some option to the generator that causes it to use handwritten definitions (exporting them from some .d.ts file) instead of generating its own for selected interfaces/types. That way, we could save ourselves the effort of having to manually edit some auto-generated file every time there is a new release of ProseMirror. On the other hand @marijn has said that there won’t be any significant API changes at least before 1.0, so maybe it is less effort to just keep them up to date manually.

The 0.18 types at DefinitelyTyped are working well for getting started. I’ll probably keep using them, because they cut down on trial-and-error while developing.

I’m working on updating the types on DefinitelyTyped based on your work @timjb. https://github.com/DefinitelyTyped/DefinitelyTyped/pulls/bradleyayers

I’m using https://github.com/bradleyayers/generate-prosemirror-types as a bit of scaffolding to streamline the process.

I’ve raised some issues that overlap with some of the issues you’ve described, and will have a go at fixing them.

I’m doing a combination of using getdocs2ts (forked from your refactor branch of buildtypedefs) and manually making changes so that we can have types in the short term, while improve the tool in the longer term.

Hey @bradleyayers! I would like to be able to generate the type signatures fully automatically in the future, without manual editing required. Therefore it would be good to have a list of things that you have fixed manually while working on the definitions for 0.21, so we can work on automating them. If there is incorrect documentation (I have found some while working on this), it’s best to fix it upstream. I also want to make it possible to have the generator use custom definitions for some declarations/functions/etc. This way we could use the custom interface declaration for DOMOutputSpec for example.

1 Like

Sounds great! I’ve been recording the manual tweaks in the github issues on https://github.com/bradleyayers/getdocs2ts, there’s some missing like the “polymorphic this types” that you described, so feel free to raise those.

1 Like

@timjb @bradleyayers Great Job! My initial idea was to provide the type-generator to @marijnso so he can include this as an additional build step and include the typings with each release of prosemirror. To provide the *.d.ts files with prosemirror packages itself would remove the necessity to update it each time on DefinitelyTyped

https://github.com/DefinitelyTyped/DefinitelyTyped/pull/16981 has been merged, so hopefully we’ll have some 0.21 types out on NPM soon. No doubt there’ll probably be some refinement over time, but it’s a great step forward!

1 Like

If we want to make some changes, should we do them on definitelytyped directly? How does it work to keep track of the changes if the types are automatically generated?

For example, I’d like to add a type for (state: EditorState, dispatch?: (tr: Transaction) => void): boolean; in prosemirror-commands rather than repeating that everywhere.

Yes just raise a pull request to DefinitelyTyped, and be sure to add a test for it. The tests aren’t automatically generated (and at this point what’s in DefinitelyTyped isn’t either, it’s hand-tweaked after auto-generation) so they serve as a good mechanism for avoiding regressions.

Any updade?