ProseMirror is now a TypeScript project

Hi all, I’ve spent the past weeks adjusting the codebase to TypeScript and (the harder part) setting up the build system and tests to work well with the new build-step requirements. This means that the code is now typed using strict TypeScript, which should make it a bit easier to explore and to ensure that changes are consistent. The types even discovered a few small issues and a bit of dead code in the existing codebase.

The new code is on github. No breaking changes should have been made to the JavaScript interface. But there were a few possibly-observable adjustments:

  • The ES modules in the packages now contain ES6 code, and are no longer compiled down to ES5. (The CommonJS modules are still ES5.)

  • Source maps are no longer included in the packages. (I understand these are occasionally useful, but they bloat the package files and slow down builds, so I’ve opted to get rid of them.)

  • The packages now come with their own TypeScript declaration files, derived from the source code.

That last one will be the main source of potential problems. There are likely enough differences between the old, hand-written types on DefinitelyTyped and the ones created from the code to break some people’s builds. For one thing, I decided against including a Schema type parameter in pretty much every type in the system, to make the types more readable and straightforward. I expect most people were defaulting that to any already, since it’d be a major pain to correctly track that throughout your code, but if you didn’t, you’ll have to adjust your types.

All the updated packages are released to npm under a beta tag, to give people a chance to migrate to them and provide feedback. Again, I don’t intend to be 100% compatible with the old types, and some porting will likely be required, but if there’s something that looks wrong or unnecessarily awkward, let me know, and I’ll see if I can fix it.

(Hey, don’t blame me for this kind of breakage—blame TypeScript’s system, which implicitly pulls in 3rd party types and then magically switches to the ones provided by the package author when a package adds types.)

The documentation comment extraction system was also changed (following the system I’m using in CodeMirror 6) to use triple-slash comments for doc comments and get the types directly from TypeScript. The reference guide has already been updated to show the result of this.

33 Likes

Epic!

1 Like

Will prosemirror-tables be ported to a TypeScript project too in the future? It has some unfixed TS typing issues for a long time. (for example this one)

Thanks for the update!

Whilst I acknowledge there are no breaking changes to the API - are there any plans to release this as a new major version, to minimise unforeseen impacts of changing type definitions?

The following code will throw error:

const setA: DecorationSet = _getDecorationSet()
const setB: DecorationSet = _getDecorationSet()
const setC: DecorationSet = setB.add(state.doc, setA.find());
Argument of type 'readonly Decoration[]' is not assignable to parameter of type 'Decoration[]'

This pattern is common when merging multiple decoration sets. Maybe we can allow DecorationSet.add (and also DecorationSet.remove, DecorationSet.create) to accept readonly Decoration[]?

Not by me. That package is currently unmaintained.

No, this is going to stay on 1.x.

Those actually mutate the arrays they get passed. But we can make find return a mutable array (patch).

1 Like

But we can make find return a mutable array (patch).

Thanks!

Not by me. That package is currently unmaintained.

Would you like to review the PR if someone else adjust prosemirror-tables to a TypeScript project?

1 Like

Currently, a ProseMirror node variable will shown as Node$1 in VSCode, which is kind of confused. Would you think it’s possible to make it more meaningful, like Node or ProsemirrorNode?

image

One thing I like from the old @types/prosemirror-view is handleDOMEvents.

type HandleDOMEventsProps = Partial<{
  [Event in keyof DocumentEventMap]: (
    view: EditorView,
    event: DocumentEventMap[Event]
  ) => boolean | void;
}> & {
  [eventName: string]: (view: EditorView, event: Event) => boolean | void;
};

export interface EditorProps {
  handleDOMEvents?: HandleDOMEventsProps;
}

With such typings, TS can infer the type of Event based the event name, so that we don’t need a temporary variable _event like this anymore:

handlers.mousedown = (view, _event) => {
  let event = _event as MouseEvent
  ...
}

However, the generated reference document could be too complex. I’m not sure it’s worth it.

1 Like

Missing a MarkView constructor prosemirror-view/index.ts at b7d6007b95da9ac2c54186b7d776de14887999d7 · ProseMirror/prosemirror-view · GitHub

No. But it’d be great if someone who’s using it steps forward as maintainer.

Oh, indeed. I’ve pushed a new version that avoids directly referencing the global Node to avoid this renaming.

In CodeMirror 6 I use an extra type name to try and keep this reasonable. Will look into it.

MarkView isn’t really something that exists in the code. NodeView is used for both nodes and marks.

1 Like

Should PluginSpec be a generic type? Which is flowed through to the generic on StateField?

I think it would be helpful when defining a plugin, and ensuring your init and apply return new plugin state in the correct shape?

P.s. Would you prefer these questions raised as GitHub issues, on a per question basis? I wonder if it would be worth having a pinned issue there for all questions on the TypeScript annoucement?

What an epic task this must have been. Brilliant news and thanks a ton!

1 Like

domEventHandlers should now be smarter about the type of the event parameter.

This doesn’t help a lot, since it seems TypeScript is usually unable to infer the type of the type parameter, but can be useful when you explicitly declare it I guess. Added, with an any default type.

Great work!

Great! :laughing:

Will the package name be renamed to @prosemirror/*?

1 Like

No, the package names are going to stay the same.

The packages with type declarations have been released to npm without beta tag now.