改进了调用联合类型的行为
在旧版本的 TypeScript 中,联合类型的可调用类型只有在参数列表完全相同时才能被调用。
tstype Fruit = "apple" | "orange";type Color = "red" | "orange";type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruittype ColorConsumer = (color: Color) => string; // consumes and describes the colorsdeclare let f: FruitEater | ColorConsumer;// Cannot invoke an expression whose type lacks a call signature.// Type 'FruitEater | ColorConsumer' has no compatible call signatures.ts(2349)f("orange");
然而,在上面的示例中,FruitEater 和 ColorConsumer 都应该能够接收字符串 "orange",并返回 number 或 string。
在 TypeScript 3.3 中,这不再是一个错误。
tstype Fruit = "apple" | "orange";type Color = "red" | "orange";type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruittype ColorConsumer = (color: Color) => string; // consumes and describes the colorsdeclare let f: FruitEater | ColorConsumer;f("orange"); // It works! Returns a 'number | string'.f("apple"); // error - Argument of type '"apple"' is not assignable to parameter of type '"orange"'.f("red"); // error - Argument of type '"red"' is not assignable to parameter of type '"orange"'.
在 TypeScript 3.3 中,这些签名的参数会被交集(intersected)在一起,以创建一个新的签名。
在上面的例子中,参数 fruit 和 color 被交集在一起,形成了一个类型为 Fruit & Color 的新参数。Fruit & Color 实际上等同于 ("apple" | "orange") & ("red" | "orange"),即 ("apple" & "red") | ("apple" & "orange") | ("orange" & "red") | ("orange" & "orange")。每一个不可能的交集都会简化为 never,最后我们只剩下 "orange" & "orange",也就是 "orange"。
注意事项
这种新行为仅在联合类型中最多只有一个类型包含多个重载,且最多只有一个类型具有泛型签名时才会生效。这意味着 number[] | string[] 上的 map 等方法(它是泛型的)仍然无法调用。
另一方面,像 forEach 这样的方法现在可以被调用了,但在 noImplicitAny 设置下可能会存在一些问题。
tsinterface Dog {kind: "dog";dogProp: any;}interface Cat {kind: "cat";catProp: any;}const catOrDogArray: Dog[] | Cat[] = [];catOrDogArray.forEach(animal => {// ~~~~~~ error!// Parameter 'animal' implicitly has an 'any' type.});
这在 TypeScript 3.3 中仍然具有更强的能力,且添加显式类型注解可以解决这些问题。
tsinterface Dog {kind: "dog";dogProp: any;}interface Cat {kind: "cat";catProp: any;}const catOrDogArray: Dog[] | Cat[] = [];catOrDogArray.forEach((animal: Dog | Cat) => {if (animal.kind === "dog") {animal.dogProp;// ...} else if (animal.kind === "cat") {animal.catProp;// ...}});
在 --build --watch 中为复合项目提供增量文件监视
TypeScript 3.0 引入了一项名为“复合项目”(composite projects)的构建结构化新功能。其部分目标是确保用户可以将大型项目拆分为较小的部分,从而实现快速构建并保持项目结构,且不牺牲既有的 TypeScript 体验。得益于复合项目,TypeScript 可以使用 --build 模式仅重新编译相关的项目集和依赖项。你可以将其视为对跨项目构建的优化。
TypeScript 2.7 还通过一种新的增量“构建器”API 引入了 --watch 模式构建。同样,这一理念在于该模式仅重新检查和重新生成已更改的文件,或其依赖关系可能影响类型检查的文件。你可以将其视为对项目内构建的优化。
在 3.3 版本之前,使用 --build --watch 构建复合项目实际上并未使用这种增量文件监视基础设施。在 --build --watch 模式下,项目中的任何更新都会强制对该项目进行完整构建,而不是确定该项目内哪些文件受到了影响。
在 TypeScript 3.3 中,--build 模式的 --watch 标志确实利用了增量文件监视功能。这意味着在 --build --watch 下构建速度将显著提升。在我们的测试中,此功能使原始的 --build --watch 构建时间减少了 50% 到 75%。你可以阅读关于此更改的原始拉取请求以查看具体数据,但我们相信大多数复合项目用户将从这里获得显著的性能收益。