动态导入表达式 (Dynamic Import Expressions)
动态 import 表达式是一项新功能,属于 ECMAScript 的一部分,允许用户在程序的任何位置异步请求模块。
这意味着你可以按条件和延迟导入其他模块和库。例如,下面是一个 async 函数,它仅在需要时才导入实用工具库:
tsasync function getZipFile(name: string, files: File[]): Promise<File> {const zipUtil = await import("./utils/create-zip-file");const zipContents = await zipUtil.getContentAsBlob(files);return new File(zipContents, name);}
许多打包工具支持根据这些 import 表达式自动拆分输出包,因此请考虑将此新功能与 esnext 模块目标一起使用。
字符串枚举 (String Enums)
TypeScript 2.4 现在允许枚举成员包含字符串初始化表达式。
tsenum Colors {Red = "RED",Green = "GREEN",Blue = "BLUE"}
需要注意的是,字符串初始化的枚举无法进行反向映射以获取原始枚举成员名称。换句话说,你不能通过 Colors["RED"] 来获取字符串 "Red"。
改进了泛型推断
TypeScript 2.4 针对泛型推断的方式引入了一些极好的改进。
将返回类型作为推断目标
首先,TypeScript 现在可以针对调用的返回类型进行推断。这可以改善你的开发体验并捕获错误。以下代码现在可以正常工作:
tsfunction arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {return a => a.map(f);}const lengths: (a: string[]) => number[] = arrayMap(s => s.length);
作为这一变动可能导致的新错误示例:
tslet x: Promise<string> = new Promise(resolve => {resolve(10);// ~~ Error!});
基于上下文类型的类型参数推断
在 TypeScript 2.4 之前,在以下示例中:
tslet f: <T>(x: T) => T = y => y;
y 的类型将为 any。这意味着程序可以通过类型检查,但你实际上可以对 y 进行任何操作,例如:
tslet f: <T>(x: T) => T = y => y() + y.foo.bar;
最后一个示例实际上并不是类型安全的。
在 TypeScript 2.4 中,右侧的函数隐式获得了类型参数,并且 y 被推断为该类型参数的类型。
如果你以类型参数的约束不支持的方式使用 y,你将正确地获得一个错误。在这种情况下,T 的约束(隐式地)为 {},因此最后一个示例将适当地报错。
泛型函数更严格的检查
TypeScript 现在尝试在比较两个单签名类型时统一类型参数。因此,你在关联两个泛型签名时会得到更严格的检查,并可能捕获一些 Bug。
tstype A = <T, U>(x: T, y: U) => [T, U];type B = <S>(x: S, y: S) => [S, S];function f(a: A, b: B) {a = b; // Errorb = a; // Ok}
回调参数的严格逆变 (Strict Contravariance)
TypeScript 一直以双向协变的方式比较参数。这有多种原因,但总的来说,在我们看到它对 Promise 和 Observable 产生的一些负面影响之前,这并不是用户的主要困扰。
TypeScript 2.4 在关联两个回调类型时加强了这一点。例如:
tsinterface Mappable<T> {map<U>(f: (x: T) => U): Mappable<U>;}declare let a: Mappable<number>;declare let b: Mappable<string | number>;a = b;b = a;
在 TypeScript 2.4 之前,这个示例会成功。当关联 map 的类型时,TypeScript 会双向关联它们的参数(即 f 的类型)。当关联每个 f 时,TypeScript 也会双向关联那些参数的类型。
在 TS 2.4 中关联 map 的类型时,语言将检查每个参数是否为回调类型;如果是,它将确保这些参数相对于当前关系以逆变的方式进行检查。
换句话说,TypeScript 现在可以捕获上述 Bug,这对于某些用户来说可能是破坏性变更,但总体上是有帮助的。
弱类型检测 (Weak Type Detection)
TypeScript 2.4 引入了“弱类型”的概念。任何只包含可选属性的类型都被视为弱类型。例如,这个 Options 类型就是一个弱类型:
tsinterface Options {data?: string;timeout?: number;maxRetries?: number;}
在 TypeScript 2.4 中,当没有属性重叠时,将任何内容分配给弱类型现在会报错。例如:
tsfunction sendMessage(options: Options) {// ...}const opts = {payload: "hello world!",retryOnFail: true};// Error!sendMessage(opts);// No overlap between the type of 'opts' and 'Options' itself.// Maybe we meant to use 'data'/'maxRetries' instead of 'payload'/'retryOnFail'.
你可以将此视为 TypeScript 在“加强”这些类型的弱保证,以捕获原本会悄无声息发生的 Bug。
由于这是一个破坏性变更,你可能需要了解解决方法,这些方法与严格对象字面量检查的解决方法相同:
- 如果属性确实存在,请声明这些属性。
- 向弱类型添加索引签名(即
[propName: string]: {})。 - 使用类型断言(即
opts as Options)。