速度提升
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。