TypeScript 3.3

改进了调用联合类型的行为

在旧版本的 TypeScript 中,联合类型的可调用类型只有在参数列表完全相同时才能被调用。

ts
type Fruit = "apple" | "orange";
type Color = "red" | "orange";
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
declare 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");

然而,在上面的示例中,FruitEaterColorConsumer 都应该能够接收字符串 "orange",并返回 numberstring

在 TypeScript 3.3 中,这不再是一个错误。

ts
type Fruit = "apple" | "orange";
type Color = "red" | "orange";
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
declare 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)在一起,以创建一个新的签名。

在上面的例子中,参数 fruitcolor 被交集在一起,形成了一个类型为 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 设置下可能会存在一些问题。

ts
interface 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 中仍然具有更强的能力,且添加显式类型注解可以解决这些问题。

ts
interface 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%你可以阅读关于此更改的原始拉取请求以查看具体数据,但我们相信大多数复合项目用户将从这里获得显著的性能收益。

TypeScript 文档是一个开源项目。请发送 Pull Request 帮助我们改进这些页面 ❤

此页面的贡献者
DRDaniel Rosenwasser (51)
OTOrta Therox (12)
NSNick Schonning (1)
JBJack Bates (1)
RARiley Avron (1)
3+

最后更新:2026 年 3 月 27 日