8
votes

I published two Javascript libraries on npm and users have asked for TypeScript type definitions for both of them. I don't use TypeScript myself and I have no plans to rewrite those libraries in TypeScript, but I'd still like to add the type definition files if only for better IntelliSense code completion. I'm looking for some advice with this.

I started with reading the docs of the DefinitelyTyped project and the documentation on publishing a declaration file for an npm package. Both sources state that "publishing to the @types organization on npm" is the preferred approach for projects not written in TypeScript.

Why is that preferred over publishing type definitions alongside the library itself via the types field in package.json? I really don't see the point in involving a third party in this. It seems like updating the type definitions and versioning them is just more complicated this way.

Quotes from the documentation referenced above (emphasis mine)

From DefinitelyTyped:

If you are the library author and your package is written in TypeScript, bundle the autogenerated declaration files in your package instead of publishing to Definitely Typed.

From typescriptlang.org:

Now that you have authored a declaration file following the steps of this guide, it is time to publish it to npm. There are two main ways you can publish your declaration files to npm:

  • bundling with your npm package, or
  • publishing to the @types organization on npm.

If your package is written in TypeScript then the first approach is favored. Use the --declaration flag to generate declaration files. This way, your declarations and JavaScript will always be in sync.

If your package is not written in TypeScript then the second is the preferred approach.

Both seem to say:

if (isAuthor && lang === "typescript")
  bundle();
else
  publishOnDefinitelyTyped();
1
Even if a package is written in pure JavaScript, if the package authors are maintaining the declarations themselves, they should be distributed as part of it. Many libraries do this, such as moment, and it greatly simplifies versioning and dependency management for both the authors and their dependents. I think you are reading guidelines that presuppose that you don't control the package being typed and are applying them too broadly.Aluan Haddad
@AluanHaddad What you say totally echoes what I thought. I didn't look at moment.js, but I saw that some core Vue.js plugins written in JS also publish their own declaration files like you described. Maybe I read the quotes wrong, but I understand them as at least implying that DefinitelyTyped is preferred. No justification is given though...bernie
By the way, even if it doesn't answer your question, I just tell you that you can generate type definition from docs, if they're written good. So maybe it requires smaller effort than you thinkCristian Traìna
@bernie Are you accepting contributions for definitions? I'd be happy to take partAvin Kavish
@AluanHaddad If you write this up as an answer, I'll accept itbernie

1 Answers

3
votes

Type declaration publishing guides seem a bit outdated and sparse in several areas.

I'll try to compare both scenarios in detail.

1. Types bundled together with the npm package

1.1. From package consumer perspective

1.1.1. Pros

In general, package bundled types are more convenient thanks to streamlined dependency management.

  • no need for additional @types dependency (adding package dependency)
  • no need to synchronize versions between the package and it's types (upgrading package dependency)

1.1.2. Cons

  • limited ways to opt-out from using package bundled types

    Involves cases when the consumer needs to modify or substitute type declarations.

    The process can be considerably problematic in projects with opinionated build setups, due to the already limited configuration options.

1.2. From package author perspective

1.2.1. Pros

  • library owner can release patches and updates of type declarations at his will, at any frequency or schedule
  • no restrictions on third party or external type dependencies
  • providing concurrent support for multiple versions is carried out in the same manner as for the actual code

1.2.2. Cons

Having types bundled with package means that there are in fact two API contracts published each time a version is released.

Example:

Let's assume a library which aims to conform to semver versioning.

  • latest release -> 1.0.0
  • major version is bumped due to a breaking change -> 2.0.0
  • a critical bug in type declarations is reported, the release is broken for a group of users with typescript projects
  • a fix in types is a breaking change

The options for next version are:

A. 2.X.X -> violates semver rules for type declarations

B. 3.0.0 -> violates semver rules for the actual code

It's likely that there are numerous variations of such scenario.

2. Publishing to Definitely typed repository

2.1. From package consumer perspective

2.1.1. Pros

  • simple opt-out via removal of @types dependency

2.1.2. Cons

  • package consumer is responsibile for keeping the package and related types versions in sync

2.2. From package author perspective

2.2.1. Pros

  • types have no impact on the package's release cycle

  • DT repo comes with two extra traits:

    • dts-lint library for type assertions and type testing
    • an in-deep performance and compiler footprint analysis, including a diff between the latest package version and the package after PR modifications.

    First tool can be incorporated into another package repo with minor effort. I'm not sure if the analysis can be replicated in one's own repository, but it contains a lot of valuable data.

2.2.2. Cons

  • non-standard way of supporting past releases

  • type release schedule constrained by DT review and release cycles

    Assuming that the DefinitelyTyped PR creator is the @types package owner, it usually takes between one to two days before the PR is merged. Additionally, there's a minor delay before types-publisher updates PR related @types npm package.

    Additional review process is involved in cases when the PR is author's first contribution to a given package.

  • using external dependencies

    TypeScript handbook says:

    If your type definitions depend on another package:

    Don’t combine it with yours, keep each in their own file.

    Don’t copy the declarations in your package either.

    Do depend on the npm type declaration package if it doesn’t package its declaration files.

    Judging by the amount of redundant utility types, these are hardly respected.

    The author of type declarations is allowed to use adjacent DT repository types. Depending on packages from beyond this list requires them to be on types-publisher whitelist.

    New packages can be whitelisted by submitting a PR to types-publisher. It took over two weeks for my PR to be merged. I don't know if that's usual since I had submitted a single PR.

  • DT repo volume

    I have no cross-IDE comparison or experience, but as far as JetBrains IDEs are concerned, the memory footprint of fully indexed DT repo project made the IDE unusable.

    Disabling recompilation on changes helps to some extent. The frustrating IDE experience can be worked around by removing DT repo contents that are not relevant to the package of interest.