import elision(import 消除)
我们都知道typescript可以简单理解成在js的基础上新增的一套静态类型检测,在ts编译时,当编译器判断 import 的是type时,编译后会将type自动消除,这就是 import 消除,例如下面这个例子:
import { foo, FooParam } from "./foo";
const param: FooParam = {
}
foo(param)
以上代码编译器编译结果如下:
var foo_1 = require("./foo");
var param = {};
foo_1.foo(param);
可以看到编译器认为FooParam是作为type类型使用所以编译之后将代码消除了, 某些高级场景下 import elision 可以起到非常好的效果,看以下这个例子:
import Index from './index' // 这行代码会被省略掉
const pageA = import('./index.js') as Promise<typeof Index>;
console.log(pageA.barProperty)
以上这个例子在开发单页应用的时候会经常遇到,我们在特定场景下需要动态加载一个子页面,我们想要给 import() 的值加入代码提示,这个时候就可以用以上这种方式,因为 Index 在这里是作为类型使用的,所以在ts编译之后这行代码会被省略掉。
这里直接调用tsc编译的结果如下:
Promise.resolve().then(function () { return require('./index.js'); });
在大部分场景下 import elision 都非常好用,但是在某些场景下我们并不想将模块完全消除,例如以下例子
// ./service.ts
export class Service {
// ...
}
register("globalServiceId", Service);
// ./consumer.ts
import { Service } from "./service.ts"; // 这一行会省略
inject("globalServiceId", function (service: Service) {
// do stuff with Service
});
在上面这个例子中 因为 Service 作为类型使用了,导致 service.ts 中的 register(副作用)函数没有执行,这个就不是我们希望看到的了
type-only import
这对import elision的问题,3.8版本中做了一次针对优化,主要是通过新增语法糖的方式将 import elision 这种自动消除的功能拆分为交给用户通过增type语法糖来决定要不要消除模块引用,目前ts3.8版本也已经发布正式版了, 可以放心使用。 具体使用看以下例子,我们只需要针对上述例子对consumer.ts进行改造:
import type { Service } from "./service.ts"; // 这里新增type语法糖
import '/service.ts'
inject("globalServiceId", function (service: Service) {
// do stuff with Service
});
在存在分歧的地方,只要使用 type语法糖来拆分模块需不需要拆分就可以解决这个问题,以上代码编译结果如下:
require("./service.ts");
inject("globalServiceId", function (service) {
// do stuff with Service
});
可以看到这时service.ts 并没有没有消除。
最后,当我们想要检查自己的项目中是否有 import elision导致的执行一场bug,可以使用tsconfig中提供的 importsNotUsedAsValues 配置项, 它有三个选项 remove, preserve, error。其中 remove 是默认行为即会正常消除, preserve表示会保留,主要这个选项可能导致代码过大,不建议使用,例如上述案例:
import Index from './index' // 这行代码会被省略掉
const pageA = import('./index.js') as Promise<typeof Index>;
以上代码如果设置为preserve 会导致index.js 打包到父页面中,导致动态加载组件失败, 最后一个选项 error 表示当项目里只import 类型的时候ts编译器会报错,这在检测老得项目中是否有异常代码比较有用,一般情况默认为为remove 就可以
总结
import elision 在ts开发中大部分场景没有问题,但是在部分场景中会出现代码执行异常,这时可以考虑替换为 type-only import 的方式引入代码。如果想检测自己的代码里是否有模块被省略掉,可以将 tsconfig 中的 importsNotUsedAsValues 设置为error重新编译以下即可