从lodash的@types库学到个知识点

5,948 阅读2分钟

先提出一个问题:

有一个第三方库(比如lodash),它的@types/lodash如何兼容不同用户机器上的不同typescript版本?

我们知道typescript一直在更新,新版本总是会添加一些新的特性。那么对于lodash而言,肯定会针对新版本的typescript使用一些新的特性去声明d.ts文件,从而使用户体验更好。

但是,用户机器上安装的typescript版本是不一样的,万一lodash直接更新了@types/lodash直接用了ts新版本的特性,而老版本ts又不支持,岂不是很多没升级ts的用户会报错?

答案就是package.json里的typesVersions属性配置!

使用typesVersions选择版本

由社区的反馈还有我们的经验得知,利用最新的TypeScript功能的同时容纳旧版本的用户很困难。 TypeScript引入了叫做typesVersions的新特性来解决这种情况。

在TypeScript 3.1里使用Node模块解析时,TypeScript会读取package.json文件,找到它需要读取的文件,它首先会查看名字为typesVersions的字段。 一个带有typesVersions字段的package.json文件:

{
    "name": "package-name",
    "version": "1.0",
    "types": "./index.d.ts",
    "typesVersions": {
        ">=3.1": { "*": ["ts3.1/*"] }
    }
}

package.json告诉TypeScript去检查当前版本的TypeScript是否正在运行。

如果是3.1或以上的版本,它会找出你导入的包的路径,然后读取这个包里面的ts3.1文件夹里的内容。 这就是 { "*": ["ts3.1/*"] }的意义 - 如果你对路径映射熟悉,它们的工作方式类似。

因此在上例中,如果我们正在从"package-name"中导入,并且正在运行的TypeScript版本为3.1,我们会尝试从[...]/node_modules/package-name/ts3.1/index.d.ts开始解析。

如果是从package-name/foo导入,由会查找[...]/node_modules/package-name/ts3.1/foo.d.ts[...]/node_modules/package-name/ts3.1/foo/index.d.ts

那如果当前运行的TypeScript版本不是3.1呢?

如果typesVersions里没有能匹配上的版本,TypeScript将回退到查看types字段,因此TypeScript 3.0及之前的版本会重定向到[...]/node_modules/package-name/index.d.ts

匹配行为

TypeScript使用Node的semver ranges去决定编译器和语言版本。

多个字段

typesVersions支持多个字段,每个字段都指定了一个匹配范围。

{
    "name": "package-name",
    "version": "1.0",
    "types": "./index.d.ts",
    "typesVersions": {
        ">=3.2": { "*": ["ts3.2/*"] },
        ">=3.1": { "*": ["ts3.1/*"] }
    }
}

因为范围可能会重叠,因此指定的顺序是有意义的。 在上例中,尽管>=3.2和>=3.1都匹配TypeScript 3.2及以上版本,反转它们的顺序将会有不同的结果,因此上例与下面的代码并不等同。

{
    "name": "package-name",
    "version": "1.0",
    "types": "./index.d.ts",
    "typesVersions": {
        // 注意,这样写不生效
        ">=3.1": { "*": ["ts3.1/*"] },
        ">=3.2": { "*": ["ts3.2/*"] }
    }
}