This answer assumes the question is about taking source code (.ts) of a project written using TypeScript 4.x constructs and making type declaration files (.d.ts) emitted from them compatible with a TypeScript 3.x compiler for the benefit of the users of the package- as opposed to writing source code that uses 4.x constructs and somehow making it compatible with a 3.x compiler (the way the question is phrased is ambiguous with respect to this). I make this assumption because:
If you decide you want to use language features in your source code that aren't supported by older compiler versions, you are (whether you realize it or not) making a decision to drop support for building the project using those older compiler version. If you have a strong reason to want to support building the project using those older compilers, then I'm pretty sure you just have to not use those newer language features (or somehow convince the maintainers of the compiler to backport those changes to older compiler versions, which I think is pretty rare).
I'm not personally aware of any reasons not to upgrade your compiler version unless you have very strict security policies and require your dependencies and build tooling to be audited. I'd wager that that's pretty rare in the JavaScript development scene where the landscape is known for changing rapidly.
downlevel-dts
Note: TypeScript has a feature to "downlevel" the JavaScript it emits to convert language constructs from later versions of the ECMA Script standard to constructs that work in older versions. (see the compileOptions.target field of tsconfig). As far as I know, TypeScript itself doesn't have such a feature to downlevel the typings files it emits (including triple-slash-directives at the time of this writing), but Nathan Sanders (a maintainer of Definitely Typed) maintains an open-source project, downlevel-dts , to downlevel .d.ts files which can downlevel typings all the way down to typescript v3.4 syntax.
package.json.typesVersions and semver-ts.org
The "'Downleveling' Types" section of semver-ts.org explains how you can script downleveling types for each typescript version where new non-backwards-compatible language constructs were introduced and how to tell a package-user's compiler which version of the types to use and where to find them: 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 a package.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: To avoid copying too much from off-site material (plagiarism), I'll summarize the steps in my own words (go read the source for the full steps with examples):
Install downlevel-dts as a dev-dependency (and some other helper tools).
Call downlevel-dts to downlevel types to whichever older type declaration versions you want to support (this can be scripted).
Update your package.json to register call your script after generating types for the more recent type declaration verion.
Register your generated older-version type declaration files in your package.json file using the typesVersions field.
Make sure the generated files are included with your package's files (update the files field, or whichever similar fields you are using).
limitations of downlevel-dts
Note that there are limitations. The following is a quote from downlevel-dts 's readme: Note that not all features can be downlevelled. For example, TypeScript 4.0 allows spreading multiple tuple type variables, at any position in a tuple. This is not allowed in previous versions, but has no obvious downlevel emit, so downlevel-dts doesn't attempt to do anything. Be sure to test the output of downlevel-dts with the appropriate version of TypeScript.
Problematic aspects of other proposed solutions:
"Use an older typescript version to emit your typings"
This assumes the asker of the question is using the TS compiler to emit typings from .ts files and doesn't work if they are maintaining them by hand, which is often the case for very large projects that were first written in JS and don't have the bandwidth to migrate to TS.
2条答案
按热度按时间ibrsph3r1#
Preface
This answer assumes the question is about taking source code (.ts) of a project written using TypeScript 4.x constructs and making type declaration files (.d.ts) emitted from them compatible with a TypeScript 3.x compiler for the benefit of the users of the package- as opposed to writing source code that uses 4.x constructs and somehow making it compatible with a 3.x compiler (the way the question is phrased is ambiguous with respect to this). I make this assumption because:
downlevel-dts
Note: TypeScript has a feature to "downlevel" the JavaScript it emits to convert language constructs from later versions of the ECMA Script standard to constructs that work in older versions. (see the
compileOptions.target
field of tsconfig).As far as I know, TypeScript itself doesn't have such a feature to downlevel the typings files it emits (including triple-slash-directives at the time of this writing), but Nathan Sanders (a maintainer of Definitely Typed) maintains an open-source project,
downlevel-dts
, to downlevel .d.ts files which can downlevel typings all the way down to typescript v3.4 syntax.package.json.typesVersions and semver-ts.org
The "'Downleveling' Types" section of semver-ts.org explains how you can script downleveling types for each typescript version where new non-backwards-compatible language constructs were introduced and how to tell a package-user's compiler which version of the types to use and where to find them:
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.)
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.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:
To avoid copying too much from off-site material (plagiarism), I'll summarize the steps in my own words (go read the source for the full steps with examples):
downlevel-dts
as a dev-dependency (and some other helper tools).downlevel-dts
to downlevel types to whichever older type declaration versions you want to support (this can be scripted).typesVersions
field.files
field, or whichever similar fields you are using).limitations of
downlevel-dts
Note that there are limitations. The following is a quote from
downlevel-dts
's readme:Note that not all features can be downlevelled. For example, TypeScript 4.0 allows spreading multiple tuple type variables, at any position in a tuple. This is not allowed in previous versions, but has no obvious downlevel emit, so downlevel-dts doesn't attempt to do anything. Be sure to test the output of downlevel-dts with the appropriate version of TypeScript.
Problematic aspects of other proposed solutions:
"Use an older typescript version to emit your typings"
This assumes the asker of the question is using the TS compiler to emit typings from .ts files and doesn't work if they are maintaining them by hand, which is often the case for very large projects that were first written in JS and don't have the bandwidth to migrate to TS.
这可能需要您使您的.ts * 源代码 * 不 * 使用 * TypeScript语言构造,这些构造是在比您必须使用的编译器更新的版本中引入的,以便在您想要发出的TypeScript语言版本中发出键入。这并不理想,因为使用较新的语言构造编写代码可能会更干净,或者仅使用较旧的构造无法完成某些任务。
“维护两种TypeScript语言版本的输入”
这似乎作出了相反的假设:项目维护手动编写JS代码的手动类型文件,而不是转换为.js并从.ts文件发出.d.ts。
这是一个 * 大 * 的维护负担(手动维护.d.ts文件已经是一个很大的维护负担了!)。对于小项目来说,这可能是可以接受的,但对于具有大型或复杂API表面的库来说,这就不是了。
mqxuamgl2#
在TypeScript中无法在支持和不支持的功能之间切换。
例如,如果您有一个基于TS 4.0和
variadic tuple types
构建的库,则无法在使用TS 3.0的包中使用它。但是,您可以维护两种类型:例如,看一看lodash或react如何维护几个版本的类型。