最近更新 VSCode 的時候,提示其內置的 Typescript 也到了 3.5.1 的版本,所以看了一下 TypeScript 3.5 的更新文檔,進行了簡單的翻譯,具體內容以下:git
typescript 3.4 版本爲了修復一個 bug 致使了類型檢查變慢,構建時間大大增長、使用編輯器時有卡頓感。github
TypeScript 3.5 作了一些優化,目前在許多增量檢查中實際上會比 TypeScript 3.3 更快。typescript
--incremental
構建TypeScript 3.4 引入了一個新的 --incremental
編譯器選項。此選項將大量信息保存到.tsbuildinfo
文件中,該文件可用於加速後續對 tsc
的調用。api
TypeScript 3.5 進行了一些優化,目前在幾百個項目的 --build
場景中,與TypeScript 3.4相比,重建的時間能夠減小68% !!瀏覽器
Omit
終於內置lib.d.ts
如今內置 Omit
了,以下安全
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
複製代碼
所以,以前若是自行添加過全局的Omit
會致使報錯:Duplicate identifier 'Omit'.
app
兩種解決方法:編輯器
Omit
,只用lib.d.ts
提供的Omit
Omit
改成export
而非放到全局TypeScript有一個特性,叫作對象文本中的多餘屬性檢查,其有助於避免 typoide
TypeScript 3.4 及以前,這一功能有一些問題,例以下面的例子不會報錯函數
type Point = {
x: number;
y: number;
};
type Label = {
name: string;
};
const thing: Point | Label = {
x: 0,
y: 0,
name: true // uh-oh!
};
複製代碼
在 TypeScript 3.5 中,類型檢查器至少驗證所提供的屬性屬於某個 union 成員,而且具備適當的類型,來保證上面的例子會報錯。
目前沒有發現這個優化會產生問題,但若是這項優化致使了代碼檢查不經過的問題,可使用這兩種方法解決:
{ myProp: SomeType } as ExpectedType
)interface ExpectedType { myProp: SomeType; [prop: string]: unknown }
)--allowUmdGlobalAccess
flagallowUmdGlobalAccess
標誌將容許從任何地方訪問 UMD 模塊中定義的全局,包括從 modules 中訪問。這多是不安全的,由於並非全部 UMD 模塊都在模塊系統中定義全局,可是在某些狀況下,您可能確實須要這種能力。例如,在瀏覽器環境中,一個UMD模塊須要在 require.js
以前加載。該模塊所以只能經過全局的方式來訪問。所以,若是你確實遇到了這種狀況,能夠用這個標誌來擺脫困境
type 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;
複製代碼
在TypeScript 3.5以前,上述檢查將會失敗,由於 S
既不能賦值給 { done: false, value: number }
也不能賦值給 { done: true, value: number }
。這種設計有時會避免一些 bug:
interface 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,
});
複製代碼
但這種設計仍是比較詭異,在TypeScript 3.5中,當用T這樣的區別屬性分配類型時,語言會更進一步,將S
這樣的類型分解爲每一個可能的類型的 union。在本例中,因爲boolean
是true
和false
的聯合,因此S
將被視爲{done: false, value: number} | {done: true, value: number}
TypeScript 3.4 改進了「返回函數的泛型函數」的類型推斷:
function 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<{}[]>
,在 TypeScript 3.4 以後,newFn
將是 <T>(x: T) => Box<T[]>
TypeScript 3.5 將這種行爲帶到了構造函數上,在某些 UI 庫(如React)中操做類組件的函數能夠更正確地操做泛型類組件:
type 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);
複製代碼
unknown
之前泛型參數的隱式約束是空對象類型{}
。在 TypeScript 3.5 中,沒有顯式約束的泛型類型參數如今隱式地約束爲unknown
。
{}
與 unknown
有下述不一樣:
{}
能夠被相似這樣k["foo"]
訪問(雖然在 --noImplicitAny
下會報錯){}
不能被賦值爲 null
和 undefined
,但 unknown
能夠{}
能夠被賦值給object
,但 unknown
能夠調用時,這意味着相似 T.toString()
等會報錯:
function foo<T>(x: T): [T, string] {
return [x, x.toString()]
// 會報錯:T 裏面沒有 toString 屬性
}
// 解決方法:增長顯式約束
function foo<T extends {}>(x: T): [T, string] {
return [x, x.toString()]
}
複製代碼
對泛型參數的類型推斷失敗時,返回的類型一樣會從 {}
變成 unknown
function parse<T>(x: string): T {
return JSON.parse(x);
}
// k has type 'unknown' - previously, it was '{}'.
const k = parse("...");
// 解決方法:
// 'k' now has type '{}'
const k = parse<{}>("...");
複製代碼
{ [k: string]: unknown }
再也不是任意對象類型的有效賦值目標TypeScript中的索引簽名 { [s: string]: any }
的行爲很特別:它是任何對象類型的有效賦值目標。這是一個特殊的規則,由於帶有索引簽名的類型一般不會產生這種行爲。
在以前 unknown
在這種狀況下的表現與 any
相同 ,{ [s: string]: unknown } 一樣是任意對象類型的有效賦值目標
let dict: { [s: string]: unknown };
// Was okay
dict = () => {};
複製代碼
通常來講,這個規則是有意義的——隱含的約束「它的全部屬性都是unknown
的某個子類型」對於任何對象類型都是很是正確的。然而,在TypeScript 3.5中,{[s: string]: unknown} 這個特殊規則被刪掉了,緣由是上面的變動:
對泛型參數的類型推斷失敗時,返回的類型一樣會從
{}
變成unknown
declare function someFunc(): void;
declare function fn<T>(arg: { [k: string]: T }): void;
fn(someFunc);
複製代碼
在TypeScript 3.4中,依次發生以下狀況:
T
找不到可選類型T
被視爲{}
someFunc
不能賦值給 arg
,由於沒有任何特殊規則,容許給 {[k: string]:{}}
隨意賦值因爲對泛型參數的類型推斷失敗時,其類型從 {}
變成 unknown
,arg
的類型將變成{[k: string]: unknown}
,任何東西均可以賦值給它,所以調用將被錯誤地容許。因此 TypeScript 3.5 中,{ [k: string]: unknown }` 再也不是任意對象類型的有效賦值目標。
注意:普通的對象字面量不受影響:
const obj = { m: 10 };
// okay
const dict: { [s: string]: unknown } = obj;
複製代碼
以前的 { [s: string]: unknown }
,根據預期行爲,可使用幾種替代方法:
{ [s: string]: any }
{ [s: string]: {} }
object
unknown
any
TypeScript 3.4 下,如下邏輯不會報錯
type A = {
s: string;
n: number;
};
const a: A = { s: "", n: 0 };
function write<K extends keyof A>(arg: A, key: K, value: A[K]): void {
// ???
arg[key] = "hello, world";
}
// Breaks the object by putting a string where a number should be
write(a, "n", "oops");
複製代碼
在TypeScript 3.5中,這個邏輯被修復,上面的示例正確地發出了一個錯誤。
這種錯誤的大多數表示相關代碼中有潛在錯誤。若是確定沒錯,則能夠強行使用一發類型斷言。
Object.keys
將拒絕原始值類型ES5 環境下,若是調用 Object.keys 時傳入一個非對象參數,會拋出錯誤,但ES2015中,若是傳入的參數是原始類型, Object.keys 將返回 []
。
以前 TypeScript 沒注意到這種狀況,這會致使 ES5 環境下可能出問題,如今若是發現目標環境是 ES5,向 Object.keys
傳入原始類型,會報錯。
因此下述狀況下,有可能須要額外添加類型斷言:
function fn(arg: object | number, isArgActuallyObject: boolean) {
if (isArgActuallyObject) {
const k = Object.keys(arg as object);
}
}
複製代碼
由於:
函數的泛型參數將被隱式約束爲
unknown
因此下面的調用也可能所以報錯
declare function fn<T>(): T;
// Was okay in TypeScript 3.4, errors in 3.5 under --target ES5
Object.keys(fn());
複製代碼
TypeScript 3.5 讓 vscode 支持下圖的功能了:
TypeScript 3.5 讓 vscode 支持下圖的功能了:
咱們預計3.6將帶來更好的創做和使用 generators 的體驗,支持ECMAScript的 private fields proposal,以及爲「支持快速增量構建和項目引用的構建工具」提供新 api。
自 3.6 起,更新頻率將從每 2 個月發一版變爲每 3 個月發一版。
Happy hacking!
– Daniel Rosenwasser and the TypeScript team