TypeScript支持JavaScript的两种主要模块系统 — CommonJS和ES模块,允许使用关键字import和export来组织代码。TypeScript的一个区别是拥有可以像值一样导出和导入的类型声明。
例如,可以导出一个接口:
// types.ts export interface User { id: number; name: string; } // index.ts import { User } from './types'; const u: User = { id: 1, name: 'Alice' };
还支持export type、export interface、import type,这允许仅导入类型而不将代码拉入最终的bundle中,从而优化构建:
import type { User } from './types';
一个重要的特点是:在TypeScript中只能导出不会出现在最终JavaScript中的类型。
import { SomeType } from './file'和import type { SomeType } from './file'有什么区别?
错误答案:它们是一样的。
正确答案:import { SomeType } ...可能导致整个模块被导入并包含在生成的JavaScript中,即使这只是一个类型。import type { SomeType } ...确保仅在编译阶段导入类型,没有副作用或代码输出。
故事
在一个大型项目中,团队通过常规导入
import { SomeType } ...从一个有副作用的模块(例如,在require/import时运行代码)中导入类型。结果是生产构建中引入了额外的依赖和副作用,导致了错误并增加了包的大小。
故事
开发者决定通过全局变量描述一个全局类型,而不是通过类型声明,并不小心导出了同名的类型和值。这导致在多个文件中名称冲突,并在更新TypeScript后构建失败。
故事
团队无法区分export/import type的特点,错误地将类型与数据一起导出。结果是tree-shaking工具无法排除死代码,增加了bundle的大小。