速度提升
TypeScript 3.5 在类型检查和增量构建方面引入了多项优化。
类型检查速度提升
相比 TypeScript 3.4,TypeScript 3.5 在类型检查效率上包含了一些优化。这些改进在编辑器场景中尤为明显,因为类型检查驱动了代码补全列表等操作。
--incremental 改进
TypeScript 3.5 改进了 3.4 的 incremental 构建模式,通过保存关于全局状态是如何计算的信息——例如编译器设置、为什么查找文件、在何处找到文件等。在涉及数百个使用 TypeScript 项目引用的 --build 模式的项目中,我们发现与 TypeScript 3.4 相比,重建时间最多可减少 68%!
更多详情,请参见以下 pull request:
Omit 辅助类型
TypeScript 3.5 引入了新的 Omit 辅助类型,它通过从原始类型中删除某些属性来创建一个新类型。
tstype Person = {name: string;age: number;location: string;};type QuantumPerson = Omit<Person, "location">;// equivalent totype QuantumPerson = {name: string;age: number;};
这里我们能够使用 Omit 辅助类型复制 Person 的所有属性,除了 location。
更多详情,请参见 GitHub 上添加 Omit 的 pull request,以及对象剩余部分使用 Omit 的变更。
联合类型中改进的多余属性检查
在 TypeScript 3.4 及更早版本中,某些多余属性在不应该被允许的情况下被允许了。例如,即使 name 属性在 Point 和 Label 之间类型不匹配,TypeScript 3.4 仍允许在对象字面量中使用错误的 name 属性。
tstype Point = {x: number;y: number;};type Label = {name: string;};const thing: Point | Label = {x: 0,y: 0,name: true // uh-oh!};
此前,非区分联合类型不会对其成员进行任何多余属性检查,结果导致类型错误的 name 属性被漏掉了。
在 TypeScript 3.5 中,类型检查器至少会验证所有提供的属性是否属于某个联合成员并且具有相应的类型,这意味着上述示例现在会正确报错。
请注意,只要属性类型有效,部分重叠仍然是被允许的。
tsconst pl: Point | Label = {x: 0,y: 0,name: "origin" // okay};
--allowUmdGlobalAccess 标志
在 TypeScript 3.5 中,你现在可以引用 UMD 全局声明,例如:
export as namespace foo;
从任何地方(甚至是模块中)引用,使用新的 allowUmdGlobalAccess 标志。
此模式增加了混用第三方库方式的灵活性,即使在模块内也可以使用库声明的全局变量。
更多详情,请参见 GitHub 上的 pull request。
更智能的联合类型检查
在 TypeScript 3.4 及更早版本中,以下示例会失败:
tstype S = { done: boolean; value: number };type T = { done: false; value: number } | { done: true; value: number };declare let source: S;declare let target: T;target = source;
这是因为 S 不能赋值给 { done: false, value: number } 也不能赋值给 { done: true, value: number }。为什么?因为 S 中的 done 属性不够具体——它是 boolean,而 T 的每个组成部分都有一个专门为 true 或 false 的 done 属性。这就是我们所说的每个组成类型被独立检查的意思:TypeScript 不会只是将每个属性合并在一起看看 S 是否可以赋值给它。如果这样做了,一些糟糕的代码可能会漏过,如下所示:
tsinterface Foo {kind: "foo";value: string;}interface Bar {kind: "bar";value: number;}function doSomething(x: Foo | Bar) {if (x.kind === "foo") {x.value.toLowerCase();}}// uh-oh - luckily TypeScript errors here!doSomething({kind: "foo",value: 123});
然而,对于最初的例子来说,这有点太严格了。如果你计算 S 的任何可能值的精确类型,实际上你会发现它与 T 中的类型完全匹配。
在 TypeScript 3.5 中,当赋值给带有区分属性的类型(如 T 中的类型)时,语言确实会进一步将 S 之类的类型分解为每个可能的具体类型的联合。在这种情况下,由于 boolean 是 true 和 false 的联合,S 将被视为 { done: false, value: number } 和 { done: true, value: number } 的联合。
更多详情,你可以参见 GitHub 上的原始 pull request。
从泛型构造函数进行高阶类型推断
在 TypeScript 3.4 中,我们改进了当泛型函数返回函数时(如下所示)的推断:
tsfunction compose<T, U, V>(f: (x: T) => U, g: (y: U) => V): (x: T) => V {return x => g(f(x));}
将其他泛型函数作为参数时,如下所示:
tsfunction arrayify<T>(x: T): T[] {return [x];}type Box<U> = { value: U };function boxify<U>(y: U): Box<U> {return { value: y };}let newFn = compose(arrayify, boxify);
TypeScript 3.4 的推断允许 newFn 成为泛型,而不是像旧版本语言那样推断出相对无用的类型(如 (x: {}) => Box<{}[]>)。其新类型是 <T>(x: T) => Box<T[]>。
TypeScript 3.5 将此行为推广到构造函数上。
tsclass Box<T> {kind: "box";value: T;constructor(value: T) {this.value = value;}}class Bag<U> {kind: "bag";value: U;constructor(value: U) {this.value = value;}}function composeCtor<T, U, V>(F: new (x: T) => U,G: new (y: U) => V): (x: T) => V {return x => new G(new F(x));}let f = composeCtor(Box, Bag); // has type '<T>(x: T) => Bag<Box<T>>'let a = f(1024); // has type 'Bag<Box<number>>'
除了上述组合模式外,对泛型构造函数的这种新推断意味着在某些 UI 库(如 React)中操作类组件的函数,可以更正确地操作泛型类组件。
tstype ComponentClass<P> = new (props: P) => Component<P>;declare class Component<P> {props: P;constructor(props: P);}declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>;type NestedProps<T> = { foo: number; stuff: T };declare class GenericComponent<T> extends Component<NestedProps<T>> {}// type is 'new <T>(props: NestedProps<T>) => Component<NestedProps<T>>'const GenericComponent2 = myHoc(GenericComponent);
要了解更多信息,请查看 GitHub 上的原始 pull request。