Reputation: 4898
I'm publishing a Typescript package, along with its Javascript compilation and a types.d.ts
file.
My dev dependency for Typescript is at ^4.5.
"devDependencies": {
"typescript": "^4.5"
}
I had a user report a breakage in a recent update, as they are using Typescript 4.1, and my types started using a feature added in Typescript 4.4.
I don't think I can take a peerDependency
, as use of Typescript is optional.
How can I register the minimum expected version of Typescript in my package?
Upvotes: 5
Views: 1232
Reputation: 50159
You night not have to if you're willing to put in some extra work to add support for older TypeScript type syntax.
If you don't want to put in that work, you can just embrace it as a breaking change to stop supporting older emitted .d.ts file syntax. If your package follows semantic versioning, bump the major version and document in your changelog that there's a breaking change that typings now require typescript 4.4 or higher. This is consistent with the recommendations on semver-ts.org in their "supported compiler versions" section.
You can support multiple typescript language versions in your emitted .d.ts files.
See the "'Downleveling' Types" section of semver-ts.org:
When a new version of TypeScript includes a backwards-incompatible change to emitted type definitions, as they did in 3.7, the strategy of changing the types directly may not work. However, it is still possible to provide backwards-compatible types, using the combination of downlevel-dts and typesVersions. (In some cases, this may also require some manual tweaking of types, but this should be rare for most packages.)
The
downlevel-dts
tool allows you to take a.d.ts
file which is not valid for an earlier version of TypeScript (e.g. the changes to class field emit mentioned in Breaking Changes), and emit a version which is compatible with that version. It supports targeting all TypeScript versions later than 3.4.TypeScript supports using the
typesVersions
key in apackage.json
file to specify a specific set of type definitions (which may consist of one or more.d.ts
files) which correspond to a specific TypeScript version.The recommended flow would be as follows:
- Add
downlevel-dts
,npm-run-all
, andrimraf
to your dev dependencies:npm install --save-dev downlevel-dts npm-run-all rimraf
- Create a script to downlevel the types to all supported TypeScript versions:
# scripts/downlevel.sh npm run downlevel-dts . --to 3.7 ts3.7 npm run downlevel-dts . --to 3.8 ts3.8 npm run downlevel-dts . --to 3.9 ts3.9 npm run downlevel-dts . --to 4.0 ts4.0
- Update the scripts key in
package.json
to generate downleveled types generated by runningdownlevel-dts
on the output fromtsc
, and to clean up the results after publication. For example, usingember-cli-typescript
’s tooling:{ "scripts": { - "prepublishOnly": "ember ts:precompile", + "prepublish:types": "ember ts:precompile", + "prepublish:downlevel": "./scripts/downlevel.sh", + "prepublishOnly": "run-s prepublish:types prepublish:downlevel", - "postpublish": "ember ts:clean", + "clean:ts": "ember ts:clean", + "clean:downlevel": "rimraf ./ts3.7 ./ts3.8 ./ts3.9 ./ts4.0", + "clean": "npm-run-all --aggregate-output --parallel clean:*", + "postpublish": "npm run clean", } }
- Add a
typesVersions
key topackage.json
, with the following contents:This will tell TypeScript how to use the types generated by this process. Note that we explicitly include the{ "types": "index.d.ts", "typesVersions": { "3.7": { "*": ["ts3.7/*"] }, "3.8": { "*": ["ts3.8/*"] }, "3.9": { "*": ["ts3.9/*"] }, "4.0": { "*": ["ts4.0/*"] }, } }
types
key so TypeScript will fall back to the defaults for3.9
and higher.- If using the
files
key inpackage.json
to specify files to include (unusual but not impossible for TypeScript-authored packages), add each of the output directories (ts3.7
,ts3.8
,ts3.9
,ts4.0
) to the list of entries.Now consumers using older versions of TypeScript will be buffered from the breaking changes in type definition emit.
If the community adopts this practice broadly we will want to invest in tooling to automate support for managing dependencies, downleveling, and type tests. However, the core constraints of this RFC do not depend on such tooling existing, and the exact requirements of those tools will emerge organically as the community begins implementing this RFC's recommendations.
There are some limitations of things that can be downleveled with this approach, so make sure to read the docs of downlevel-dts to make sure you don't hit those edge cases.
Upvotes: 4