TypeScript 4.2 有哪些新特性?
TypeScript 4.2 發(fā)布了!對于不熟悉 TypeScript 的人來說,TypeScript 就是增加了靜態(tài)類型和類型檢查的 JavaScript。有了類型限制,你就可以精確的表達你的函數(shù)需要什么類型的參數(shù)以及返回什么類型的結(jié)果。
同時,利用 TypeScript 的類型檢查,你可以很容易避免一些常見錯誤,例如拼寫錯誤或者忘記處理 null 和 undefined。因為 TypeScript 代碼看起來就像是增加了類型的 JavaScript,所以你對 JavaScript 所知的一切知識依然適用于 TypeScript。在需要的時候,你也可以剝離類型,從而獲得純凈、可讀并且在任何地方可以運行的 JavaScript 代碼。如果你想深入了解 TypeScript , 你可以訪問這個網(wǎng)站[1]。
開始使用 TypeScript 4.2,你可以通過 NuGet[2] 安裝,或使用 npm 運行以下命令:
- npm install typescript
讓我們預覽一下 TypeScript 4.2 的新特性:
- 更智能的類型別名保持
- 元組類型中的前置/中置的擴展元素
- 更嚴格的 in 運算符檢查
- --noPropertyAccessFromIndexSignature
- 抽象構(gòu)建簽名
- 通過 --explainFiles 了解你的項目結(jié)構(gòu)
- 改進邏輯表達式中非執(zhí)行函數(shù)的檢查
- 可以將解構(gòu)變量明確標記為未使用
- 在可選屬性和字符串索引簽名之間寬松的規(guī)則
- 聲明缺少的幫助函數(shù)
- 破壞性變化
更智能的類型別名保持
TypeScript 有一種方式可以聲明新的類型叫做類型別名。如果你寫了一系列函數(shù)可以處理 string | number | boolean 三種類型的數(shù)據(jù),那么你可以定義一個類型別名,用于避免重復工作:
- type BasicPrimitive = number | string | boolean
TypeScript 曾使用一系列的規(guī)則來猜測何時應當使用類型別名,而何時又應該把所有類型都打印出來。例如,來看下以下代碼片段:
- export type BasicPrimitive = number | string | boolean;
- export function doStuff(value: BasicPrimitive) {
- let x = value;
- return x;
- }
如果我們在諸如 Visual Studio、VS Code 編輯器中或者 TypeScript 運行環(huán)境[3] 中把鼠標懸停在變量 x 上,我們就會看到一個快速展示面板,顯示出 x 的類型為 BasicPrimitive。同樣的,如果我們?yōu)檫@個方法定義一個聲明文件(.d.ts 文件),TypeScript 將會顯示 doStuff 返回值的類型為 BasicPrimitive。
但是,如果我們返回的是 BasicPrimitive 或者 undefined 會發(fā)生什么呢?
- export type BasicPrimitive = number | string | boolean;
- export function doStuff(value: BasicPrimitive) {
- if (Math.random() < 0.5) {
- return undefined;
- }
- return value;
- }
我們可以在TypeScript 運行環(huán)境[4]中觀察發(fā)生了什么。盡管我們希望看到 TypeScript 展示的 doStuff 返回值類型是 BasicPrimitive | undefined,但是實際情況是,顯示的返回值類型為 string | number | boolean | undefined!這是怎么回事?
這要歸因于 TypeScript 內(nèi)部對于類型的解析方式。當創(chuàng)建了一個聯(lián)合類型包含一個或多個其他的聯(lián)合類型時,TypeScript 會將這些類型歸一化為一個新的扁平的聯(lián)合類型 —— 此時,原本類型的信息就丟失了。類型檢查器將會查找 string | number | boolean | undefined 每種組合是否具有類型別名,即使這樣,仍可能會得到多個 string | number | boolean 類型別名的結(jié)果。
在 TypeScript 4.2 中,我們的內(nèi)部邏輯將更加智能。我們會通過保留類型的原始定義以及后續(xù)對其的更新,從而持續(xù)追蹤該類型的構(gòu)造變化。我們同時也會追蹤被鍵入其他類型別名實例的類型別名,并加以區(qū)分。
能夠根據(jù)你在代碼中使用的方式打印出這些類型,意味著對于 TypeScript 使用者來說,可以避免看到那些令人厭惡的巨型類型定義;并且這種方式可以幫助轉(zhuǎn)化出更優(yōu)質(zhì)的 .d.ts 文件輸出、錯誤信息以及在編輯器中對變量展示的快速信息和幫助等。
更詳細的內(nèi)容,可以查閱改進保留實例間聯(lián)合和相交類型別名的第一個Pull Request[5],以及隨后的保留間接別名的第二個Pull Request[6]。
元組類型中的前置/中置的擴展元素
在 TypeScript 中,元組類型最初用于對特定長度和特定元素類型進行建模。
- // 一個存放了一對數(shù)字的元組
- let a: [number, number] = [1, 2];
- // 一個存放了一個字符串、一個數(shù)字以及一個布爾值的元組
- let b: [string, number, boolean] = ["hello", 42, true];
隨著版本的更新,TypeScript 元組變得越來越復雜,因為它們還被用于對像 JavaScript 中的參數(shù)列表一樣的事務進行建模。這樣帶來的結(jié)果就是,元組類型可以包含可選元素以及擴展元素,甚至為了提高可讀性和易用性,元素還可以擁有標簽。
- // 一個包含一個或兩個字符串的元組
- let c: [string, string?] = ["hello"];
- c = ["hello", "world"];
- // 一個包含一個或兩個字符串,并且打了標簽的元組
- let d: [first: string, second?: string] = ["hello"];
- d = ["hello", "world"];
- // 一個包含擴展元素的元組 —— 前置元素至少包含兩個字符串
- // 后置元素可以包含任意多個布爾值
- let e: [string, string, ...boolean[]];
- e = ["hello", "world"];
- e = ["hello", "world", false];
- e = ["hello", "world", true, false, true];
在 TypeScript 4.2 中,擴展元素的使用特別得到了擴展。在較早的版本中,TypeScript 只允許擴展元素出現(xiàn)在元組的最后位置。
但是現(xiàn)在,擴展元素可以出現(xiàn)在元組的任意位置 —— 僅僅需要滿足一些限制條件。
- let foo: [...string[], number];
- foo = [123];
- foo = ["hello", 123];
- foo = ["hello!", "hello!", "hello!", 123];
- let bar: [boolean, ...string[], boolean];
- bar = [true, false];
- bar = [true, "some text", false];
- bar = [true, "some", "separated", "text", false];
想讓擴展元素放在元組的任意位置,唯一的限制條件就是:不能有可選元素在其后面的位置并且不能有其他的擴展元素。換句話說,每個元組只允許一個擴展元素,并且該擴展元素后序位置不能跟隨一個可選元素。
- interface Clown { /*...*/ }
- interface Joker { /*...*/ }
- let StealersWheel: [...Clown[], "me", ...Joker[]];
- // ~~~~~~~~~~ Error!
- // 擴展元素后續(xù)不能有其他的擴展元素
- let StringsAndMaybeBoolean: [...string[], boolean?];
- // ~~~~~~~~ Error!
- // 擴展元素后序不能是可選元素
這些靈活的擴展元素,可以用于對具有任意數(shù)量前置參數(shù)以及固定格式后置參數(shù)的函數(shù)進行建模。
- declare function doStuff(...args: [...names: string[], shouldCapitalize: boolean]): void;
- doStuff(/*shouldCapitalize:*/ false)
- doStuff("fee", "fi", "fo", "fum", /*shouldCapitalize:*/ true);
盡管 JavaScript 沒有任何語法可以支持前置的擴展參數(shù),我們?nèi)钥梢允褂?...args 這種元組類型來實現(xiàn)上述 doStuff 那樣具有前置擴展參數(shù)的函數(shù)。用這種方式可以對許多現(xiàn)有的 JavaScript 進行建模。
更多詳細信息,請參考這個 Pull Request[7]。
更嚴格的 in 運算符檢查
在 JavaScript 中,在 in 運算符右側(cè)使用非對象類型是一個運行時錯誤。而在 TypeScript 4.2 中,該錯誤可以在代碼設計時即被捕獲。
- "foo" in 42
- // ~~
- // 錯誤!'in' 表達式右側(cè)不能為基數(shù)據(jù)類型
這項檢查在大多數(shù)情況下都是相當保守的,如果你遇到過相關(guān)的錯誤提示,很可能是代碼出了問題。
非常感謝我們的外圍貢獻者 Jonas Hübotter[8] 提出的 Pull Request[9]。
--noPropertyAccessFromIndexSignature
在 TypeScript 首次引入索引簽名時,你只能使用諸如 person["name"] 這種 “中括號” 元素訪問語法來聲明對象的屬性。
- interface SomeType {
- /** 這是一個索引簽名 */
- [propName: string]: any;
- }
- function doStuff(value: SomeType) {
- let x = value["someProperty"];
- }
如果我們需要一個具有任意屬性的對象,這種方式將會非常麻煩。舉個例子,假設有一個 API,犯了一個常見的拼寫錯誤——在某個屬性名字后面多加了一個 s。
- interface Options {
- /** 需要排除的文件格式 */
- exclude?: string[];
- /**
- * 處理未聲明為 'any' 的所有其他屬性
- */
- [x: string]: any;
- }
- function processOptions(opts: Options) {
- // 注意這里我們故意訪問的是 `excludes` 而非 `exclude`
- if (opts.excludes) {
- console.error("The option `excludes` is not valid. Did you mean `exclude`?");
- }
- }
為了使這些類型更易用,前不久,TypeScript 允許了使用 “.” 方法訪問具有字符串索引簽名對象(例如:person.name)的屬性的語法。這也使得從現(xiàn)有 JavaScript 代碼過渡為 TypeScript 代碼變得容易。
但是,放松限制同時也意味著拼寫錯誤導致的顯示聲明屬性的錯誤訪問會更加容易。
- function processOptions(opts: Options) {
- // ...
- // 注意,這次我們是 “無意間” 訪問了 `excludes`
- // 糟糕!完全奏效。
- for (const excludePattern of opts.excludes) {
- // ...
- }
- }
某些情況下,用戶只希望在顯示聲明的索引簽名中進行訪問——他們希望在使用點方法訪問對象屬性時,如果該屬性不具有顯示聲明,則應當返回錯誤信息。
這就是 TypeScript 引入新的標識 --noPropertyAccessFromIndexSignature 的目的。開啟這么模式,你將選擇使用 TypeScript 舊的驗證行為,從而在上述過程中拋出錯誤。這個新的設置不受嚴格模式的限制,因為我們相信用戶會發(fā)現(xiàn)它在某些特定代碼中會很有用。
你可以通過閱讀這個 Pull Request[10] 獲取對這個功能更詳細的了解。同時,我們還要非常感謝 Wenlu Wang[11] 向我們提交了這個 Pull Request。
抽象構(gòu)建簽名
TypeScript 允許我們標記一個類為抽象類。這將告訴 TypeScript 該類只能被其他類擴展,并且擴展類必須包含確切的屬性才能實例化。
- abstract class Shape {
- abstract getArea(): number;
- }
- // 錯誤! 不能實例化一個抽象類。
- new Shape();
- class Square extends Shape {
- #sideLength: number;
- constructor(sideLength: number) {
- this.#sideLengthsideLength = sideLength;
- }
- getArea() {
- return this.#sideLength ** 2;
- }
- }
- // 可以正確執(zhí)行
- new Square(42);
為了確保這條限制在新建抽象類的過程中始終有效,你不能將抽象類賦值給任何需要構(gòu)造簽名的對象。
- interface HasArea {
- getArea(): number;
- }
- // 錯誤!不能將抽象構(gòu)造類型賦值給非抽象構(gòu)造類型
- let Ctor: new () => HasArea = Shape;
如果我們原意是執(zhí)行 new Ctor 這樣的代碼,拋出異常是正確的行為。但如果我們是想編寫 Ctor 的子類,這種限制就顯得過于嚴格。
- functon makeSubclassWithArea(Ctor: new () => HasArea) {
- return class extends Ctor {
- getArea() {
- // ...
- }
- }
- }
- let MyShape = makeSubclassWithArea(Shape);
同樣的,它同樣不能與內(nèi)置的幫助類,例如 InstanceType,配合使用。
- // 錯誤!
- // 類型 'typeof Shape' 不滿足于約束條件 'new (...args: any) => any'。
- // 不能將抽象構(gòu)造類型賦值給非抽象構(gòu)造類型
- type MyInstance = InstanceType<typeof Shape>;
這就是為什么 TypeScript 4.2 要允許你在構(gòu)造簽名中指定抽象指示器。
- interface HasArea {
- getArea(): number;
- }
- // 成功!
- let Ctor: abstract new () => HasArea = Shape;
- // ^^^^^^^^
為構(gòu)造簽名增加抽象指示器,意味著你可以將其在抽象構(gòu)造函數(shù)中傳遞。這不會阻止你向其傳遞其他的 “具象” 類或構(gòu)造函數(shù),該指示器實際上只是表示該類不會直接運行構(gòu)造函數(shù),因此可以安全地傳遞任何一種類型的類。
此項功能使我們可以用帶有抽象類的方式編寫 mixin 工廠函數(shù)。舉例來說,在下面這段代碼中,我們可以使用包含了抽象類 SuperClass 的 mixin 函數(shù) withStyles。
- abstract class SuperClass {
- abstract someMethod(): void;
- badda() {}
- }
- type AbstractConstructor<T> = abstract new (...args: any[]) => T
- function withStyles<T extends AbstractConstructor<object>>(Ctor: T) {
- abstract class StyledClass extends Ctor {
- getstyles() {
- // ...
- }
- }
- return StyledClass;
- }
- class SubClass extends withStyles(SuperClass) {
- someMethod() {
- this.someMethod()
- }
- }
請注意,withStyle 展示的是一條特定規(guī)則,必須將擴展自抽象構(gòu)造函數(shù)(如 Ctor) 的類 (如 StyledClass) 也聲明為抽象類。這是因為無法知道是否傳入了具有更多抽象成員的類,并且也無法知道子類是否實現(xiàn)了所有抽象成員。
你可以在這個Pull Request[12]里,查看更多抽象構(gòu)造簽名的內(nèi)容。
通過 --explainFiles 了解你的項目結(jié)構(gòu)
對于 TypeScript 用戶來說,一個令人驚訝又常見的場景是被問到:“為什么 TypeScript 包含這個文件?” 推斷程序文件是一個復雜的過程,這就是為什么一個特定組合的 lib.d.ts 很有必要,為什么 node_modules 中特定的文件需要被包含以及為什么有些文件我們認為應該被排除但是結(jié)果卻被包含。
這也就是為什么 TypeScript 現(xiàn)在提供了 --explainFiles 標志。
- tsc --explainFiles
當你啟用這個選項,TypeScript 編譯器將給出一些非常冗長的輸出,用于說明某個文件為什么會出現(xiàn)在程序里。為了方便的閱讀,你可以把輸出內(nèi)容存入一個文件,或者通過管道輸出到更容易閱讀的程序中。
- # 將輸出內(nèi)容存入文件
- tsc --explainFiles > expanation.txt
- # 通過管道將輸出內(nèi)容發(fā)送到工具程序例如`less`,或者像 VS Code 這種編輯器
- tsc --explainFiles | less
- tsc --explainFiles | code -
通常,輸出內(nèi)容首先會列出包含 lib.d.ts 的原因,然后是本地文件,最后是 node_modules 文件。
- TS_Compiler_Directory/4.2.2/lib/lib.es5.d.ts
- Library referenced via 'es5' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2015.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2015.d.ts
- Library referenced via 'es2015' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2016.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2016.d.ts
- Library referenced via 'es2016' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2017.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2017.d.ts
- Library referenced via 'es2017' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2018.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2018.d.ts
- Library referenced via 'es2018' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2019.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2019.d.ts
- Library referenced via 'es2019' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2020.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.es2020.d.ts
- Library referenced via 'es2020' from file 'TS_Compiler_Directory/4.2.2/lib/lib.esnext.d.ts'
- TS_Compiler_Directory/4.2.2/lib/lib.esnext.d.ts
- Library 'lib.esnext.d.ts' specified in compilerOptions
- ... More Library References...
- foo.ts
- Matched by include pattern '**/*' in 'tsconfig.json'
目前,我們還沒發(fā)保證輸出的格式 —— 在后續(xù)的更新中可能會不斷變化。值得一提的是,如果你對格式改進有任何好的建議,我們都非常感興趣。
更多信息,請參照原始 Pull Request[13]。
改進邏輯表達式中非執(zhí)行函數(shù)的檢查
多虧了 Alex Tarasyuk[14] 的進一步改進,TypeScript 非執(zhí)行函數(shù)檢查現(xiàn)在適用于 && 和 || 表達式。
現(xiàn)在在 --strictNullChecks模式下,以下代碼會報錯:
- function shouldDisplayElement(element: Element) {
- // ...
- return true;
- }
- function getVisibleItems(elements: Element[]) {
- return elements.filter(e => shouldDisplayElement && e.children.length)
- // ~~~~~~~~~~~~~~~~~~~~
- // 該條件始終會返回 true,因為該函數(shù)已經(jīng)被定義了。
- // 你是否是想執(zhí)行它來代替?
- }
更多信息,詳見 Pull Request[15]。
可以將解構(gòu)變量明確標記為未使用
感謝 Alex Tarasyuk[16] 的另一個 Pull Request,你現(xiàn)在可以通過在解構(gòu)變量前加上下劃線,從而將其標記為未使用變量。
- let [_first, second] = getValues();
之前的版本中,如果 _first 在之后的代碼中沒有被使用,那么 TypeScript 會拋出一個 noUnusedLocals 錯誤?,F(xiàn)在,TypeScript 會認識到 _first 只聲明不調(diào)用是有意為之。
詳情可見這個 Pull Request [17]。
在可選屬性和字符串索引簽名之間寬松的規(guī)則
字符串索引簽名是用于鍵入類似字典對象的方式 —— 當你想允許該對象包含使用任意鍵名。
- const movieWatchCount: { [key: string]: number } = {};
- function watchMovie(title: string) {
- movieWatchCount[title] = (movieWatchCount[title] ?? 0) + 1;
- }
當然,現(xiàn)在該典中沒有包含任何電影標題,movieWatchCount[title] 將返回 undefined (TypeScript 4.1 中新增了一個選項 --noUncheckedIndexedAccess 會給字符串索引簽名自動添加 undefined 可選類型)。即使很明顯,movieWatchCount 必將在之后包含某些字符串,但由于存在 undefined,之前的版本的 TypeScript ,仍然會將可選對象屬性視為無法分配給其他兼容的索引簽名。
- type WesAndersonWatchCount = {
- "Fantastic Mr. Fox"?: number;
- "The Royal Tenenbaums"?: number;
- "Moonrise Kingdom"?: number;
- "The Grand Budapest Hotel"?: number;
- };
- declare const wesAndersonWatchCount: WesAndersonWatchCount;
- const movieWatchCount: { [key: string]: number } = wesAndersonWatchCount;
- // ~~~~~~~~~~~~~~~ 錯誤!
- // 類型 'WesAndersonWatchCount' 不能賦值給 '{ [key: string]: number; }'.
- // 屬性 '"Fantastic Mr. Fox"' 和索引簽名不兼容。
- // 類型 'number | undefined' 不能賦值給 'number'.
- // 類型 'undefined' 不能賦值給類型 'number'. (2322)
TypeScript 4.2 允許這個指派。但是,它不允許分配類型為 undefined 的非可選屬性,也不允許將 undefined 寫入特定鍵:
- type BatmanWatchCount = {
- "Batman Begins": number | undefined;
- "The Dark Knight": number | undefined;
- "The Dark Knight Rises": number | undefined;
- };
- declare const batmanWatchCount: BatmanWatchCount;
- // TypeScript 4.2. 依然會報錯
- // `undefined` 指揮在屬性被標記為可選時才會被忽略。
- const movieWatchCount: { [key: string]: number } = batmanWatchCount;
- // TypeScript 4.2. 依然會報錯
- // 索引簽名不允許顯示定義為 `undefined`。
- movieWatchCount["It's the Great Pumpkin, Charlie Brown"] = undefined;
新規(guī)則同樣不適用于數(shù)字索引簽名,因為它們被假定為類似數(shù)組的密集型數(shù)據(jù)結(jié)構(gòu)。
你可以通過閱讀這個 Pull Request[18] 更好的理解這條規(guī)則。
聲明缺少的幫助函數(shù)
感謝 Alex Tarasyuk[19] 實現(xiàn)的來自社區(qū)的 Pull Request[20],我們現(xiàn)在有了一個在聲明新函數(shù)和方法時的快速修復。
圖片
破壞性變化
我們始終致力于將發(fā)布中的破壞性變化降至最低。TypeScript 4.2 包含一些破壞性變化,但我們認為它們在升級過程中時可控的。
lib.d.ts 升級
如同每個版本的 TypeScript 升級,lib.d.ts 的聲明(特別是為 Web 上下文生成的聲明)都會發(fā)生改變。升級的變化有很多,但是最終可能最具有破壞性的是 Intl 和 ResizeObserver。
onImplicitAny 錯誤適用于寬松的 yield 表達式
當捕獲了 yield 表達式的值,但是 TypeScript 不能立刻分辨出你打算接受的類型是哪種時(舉個例子:yield 表達式未按上下文類型輸入),TypeScript 現(xiàn)在會發(fā)出類型隱式聲明為 any 的錯誤。
- function* g1() {
- const value = yield 1;
- // ~~~~~~~
- // 錯誤!
- // 'yield' 表達式隱式返回 'any' 類型
- // 因為其包含缺少返回類型注釋的生成器。
- }
- function* g2() {
- // 正確。
- // `yield 1` 的結(jié)果未被使用。
- yield 1;
- }
- function* g3() {
- // 正確。
- // `yield 1` 根據(jù)上下文被定義為 'string' 類型。
- const value: string = yield 1;
- }
- function* g3(): Generator<number, void, string> {
- // 正確。
- // 通過 'g3' 的顯式返回結(jié)果,
- // TypeScript 可以知道 'yield 1' 的類型
- const value = yield 1;
- }
詳情可見這個 Pull Request[21]。
擴展的未執(zhí)行函數(shù)檢查
如上文所述,當啟用 --strictNullChecks 模式時,未執(zhí)行函數(shù)檢查將會在 && 和 || 表達式中一致執(zhí)行。這可能會成為一個潛在的破壞性,但通常表示現(xiàn)有代碼中存在邏輯錯誤。
JavaScript 中的類型參數(shù)不會被解析未類型參數(shù)
JavaScript 中已經(jīng)不允許使用類型參數(shù),但是在 TypeScript 4.2 中,解析器將以更符合規(guī)范的方式解析它們。所以,當在 JavaScript 中編寫以下代碼時:
- f<T>(100)
TypeScript 會把它解析為以下代碼:
- (f < T) > (100)
當你利用 TypeScript 的 API 來解析 JavaScript 文件中的類型構(gòu)造,你可能會感到困擾。
in 運算符不再允許右側(cè)的值為基類型
- "foo" in 42
- // ~~
- // 錯誤!'in' 表達式右側(cè)不允許是基類型
詳細請參考這個 Pull Request[22]。
元組中的擴展運算符大小受限
TypeScript 中元組類型可以由任何類型的擴展運算語法生成
- // 由擴展元素生成的元組類型
- type NumStr = [number, string];
- type NumStrNumStr = [...NumStr, ...NumStr];
- // 數(shù)組擴展運算
- const numStr = [123, "hello"] as const;
- const numStrNumStr = [...numStr, ...numStr] as const;
有時,這些元組類型可能會意外的變得巨大,這會使類型檢查花費很長時間。為了防止類型檢查掛起(這在編輯器場景中尤為糟糕),TypeScript 使用一個限制器來避免這個情況的發(fā)生。
你可以在這個 Pull Request[23] 中查看詳情。
.d.ts 擴展名的文件不能被 Import
- // 必須改為類似于以下類型:
- // - "./foo"
- // - "./foo.js"
- import { Foo } from "./foo.d.ts";
導入路徑應反映加載程序在運行時將執(zhí)行的操作。以下幾種導入都是等價的:
- import { Foo } from "./foo";
- import { Foo } from "./foo.js";
- import { Foo } from "./foo/index.js";
恢復模板字面推斷
這個改變是從 TypeScript 4.2 Beta 版中刪除了一個功能。如果你還沒有升級到我們最新的一個穩(wěn)定版,你可以不必關(guān)注這個。但是,也許你也有興趣簡單了解一下。
TypeScript 4.2 Beta 版包含了一個對模板字符串推斷的更改。在這個更改中,模板字符串字面或者被定義為給定的模板字符串類型或者簡化為多個字符串字面類型。這些類型當被賦值給變量時,都會被擴大為字符串類型。
- declare const yourName: string;
- // 'bar' 是常量。
- // 它擁有類型 '`hello ${string}`'.
- const bar = `hello ${yourName}`;
- // 'baz' 是變量
- // 它擁有類型 'string'.
- let baz = `hello ${yourName}`;
這類似于字符串字面推斷的工作方式。
- // 'bar' 的類型是 '"hello"'.
- const bar = "hello";
- // 'baz' 的類型是 'string'.
- let baz = "hello";
因此,我們認為擁有字符串類型的模板字符串表達式應該為 “常量”。但是,從我們所見所得的情況來看,這并不總是可取的。
作為回應,我們恢復了這個功能(以及潛在的破壞性)。如果你確實想要給一個模板字符串表達式定義為字面量類型,你可以在它的后面增加 as const。
- declare const yourName: string;
- // 'bar' 擁有類型 '`hello ${string}`'.
- const bar = `hello ${yourName}` as const;
- // ^^^^^^^^
- // 'baz' 擁有類型 'string'.
- const baz = `hello ${yourName}`;
TypeScript lift Callback 在 visitNode 中使用不同的類型
TypeScript 具有帶lift功能的 visitNode 函數(shù)?,F(xiàn)在,lift 需要一個只讀的 Node[] 而非 NodeArray<Node>。
從技術(shù)上講,這是一個API重大更改,您可以在此處閱讀更多內(nèi)容。