我們很高興地宣佈,TypeScript 3.6正式面世了!
有些人可能還不太瞭解TypeScript,這裏做一個簡單介紹:TypeScript是一種基於JavaScript的語言,在後者的基礎上添加了可選的靜態類型;TypeScript編譯器可以檢查這些類型以捕獲程序中的常見錯誤(例如屬性拼寫錯誤和函數調用錯誤等);然後可以使用TypeScript編譯器和Babel等工具將基於最前沿規範編寫的TypeScript代碼轉換爲符合標準的ECMAScript代碼,後者可在任何瀏覽器或運行時(甚至是隻支持ES3或ES5的舊版本)上運行。
TypeScript不僅具備類型檢查和較新的ECMAScript功能,其編輯工具也是業界一流水平,是TypeScript項目不可或缺的一部分;多種類型的編輯器爲TypeScript提供了代碼自動完成、重構和快速修復等功能。如果你在Visual Studio或Visual Studio Code中編輯過JavaScript文件,其實這些體驗是由TypeScript提供的,所以你可能已經在不知情的情況下開始使用TypeScript了!
你可以查看TypeScript官方網站瞭解更多信息。TypeScript可以通過NuGet獲取,或在npm中鍵入以下命令:
npm install -g typescript
可選的編輯器有:
-
Visual Studio Code的說明。
不久的將來我們還會提供其他編輯器的支持。
下面來看看3.6版中的更新內容吧!
語言和編譯器
更嚴格的生成器
TypeScript 3.6對迭代器和生成器函數的檢查更嚴格了。在早期版本中,用戶使用生成器時無法判斷一個值是從生成器yield還是返回的。
function* foo() {
if (Math.random() < 0.5) yield 100;
return "Finished!"
}
let iter = foo();
let curr = iter.next();
if (curr.done) {
// TypeScript 3.5及之前版本把它當成 'string | number'.
// 它應該知道這是 'string' ,因爲 'done' 是 'true'!
curr.value
}
此外,生成器之前會假定yield的類型總是any。
function* bar() {
let x: { hello(): void } = yield;
x.hello();
}
let iter = bar();
iter.next();
iter.next(123); // oops! runtime error!
TypeScript 3.6中的檢查器現在知道第一段代碼中的curr.value的正確類型應該是string,並且在第二段代碼中調用next()時會正確地報錯。這是因爲Iterator和IteratorResult類型聲明中現在引入了一些新的類型參數,且新版TypeScript會用Generator這個新類型來表示生成器。
Iterator類型現在允許用戶指定yield類型、返回的類型以及next可以接受的類型。
interface Iterator<T, TReturn = any, TNext = undefined> {
// 接收 0 或 1 參數 - 不接收 'undefined'
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
在此基礎上,新的Generator類型是一個Iterator,它總是同時存在return和throw方法,並且也是可迭代的。
interface Generator<T = unknown, TReturn = any, TNext = unknown>
extends Iterator<T, TReturn, TNext> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
Symbol.iterator: Generator<T, TReturn, TNext>;
}
爲了區分返回值和生成值,TypeScript 3.6將IteratorResult類型轉換爲差別聯合類型:
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
簡而言之,這意味着你在直接處理迭代器時能夠適當地縮小迭代器的值。
爲了正確表示可以從調用next()傳遞給生成器的類型,TypeScript 3.6還可以在生成器函數主體內推斷出某些yield用法。
function* foo() {
let x: string = yield;
console.log(x.toUpperCase());
}
let x = foo();
x.next(); // 對 'next' 的第一個調用都會被忽略
x.next(42); // error! 'number' 無法被分配給 'string'
如果你更喜歡顯式方法,那麼還可以強制可以用yield表達式執行返回、yield和計算的值的類型使用顯式返回類型。下面的例子中,next( )只能用布爾值調用,並且根據done的值,value可以是string或number。
/**
* - yields numbers
* - returns strings
* - can be passed in booleans
*/
function* counter(): Generator<number, string, boolean> {
let i = 0;
while (true) {
if (yield i++) {
break;
}
}
return "done!";
}
var iter = counter();
var curr = iter.next()
while (!curr.done) {
console.log(curr.value);
curr = iter.next(curr.value === 5)
}
console.log(curr.value.toUpperCase());
// prints:
//
// 0
// 1
// 2
// 3
// 4
// 5
// DONE!
這部分更改的詳情可參閱github。
更準確的數組擴展
在目標爲ES2015之前的版本中,諸如for/of循環和數組擴展之類的結構用的發射有些複雜。因此TypeScript默認使用更簡單的發射,只支持數組類型,並支持使用–downlevelIteration標誌在其他類型上迭代。在此標誌下發出的代碼更準確,但體積也大得多。
默認情況下–downlevelIteration是關閉的,因爲以ES5爲目標的用戶多數只使用帶數組的迭代結構。但某些邊緣情況下只支持數組的發射還是有一些可見的差異。
例如,以下示例
[...Array(5)]
等同於下列數組:
[undefined, undefined, undefined, undefined, undefined]
但是,TypeScript會將原始代碼轉換爲下面的代碼:
Array(5).slice();
它們是有些不一樣的。Array(5)生成一個長度爲5的數組,但沒有定義的屬性槽!
1 in [undefined, undefined, undefined] // true
1 in Array(3) // false
當TypeScript調用slice()時,它還會創建一個其索引尚未設置的數組。
這可能不太好理解,但其實有許多用戶遇到了這種麻煩。TypeScript 3.6不再使用slice()和內置函數,而是引入了一個新的__spreadArrays
助手,將ECMAScript 2015的內容在較舊的目標中模擬出來,不用 --downlevelIteration。 __spreadArrays也可以在tslib中使用(如果你想縮小包的體積,那麼非常值得一試)。
有關更多信息,請參閱資料。
改進了Promise相關的用戶體驗
Promise是當今最常用的異步數據方法之一。然而面向Promise的API通常會讓用戶感到困惑。TypeScript 3.6引入了一些改進,避免用戶錯誤處理Promise。
例如,在將Promise的內容傳遞給另一個函數之前,人們往往會忘記.then()或await這部分內容。TypeScript現在有了對應的錯誤消息,並告知用戶他們可能應該使用await關鍵字。
interface User {
name: string;
age: number;
location: string;
}
declare function getUserData(): Promise<User>;
declare function displayUser(user: User): void;
async function f() {
displayUser(getUserData());
// ~~~~~~~~~~~~~
// 類型 'Promise<User>' 的參數不能分配給類型 'User'的參數.
// ...
// Did you forget to use 'await'?
}
在await或.then()一個Promise之前就嘗試訪問方法也是很常見的錯誤。這類錯誤很多,我們都做了改進。
async function getCuteAnimals() {
fetch("https://reddit.com/r/aww.json")
.json()
// ~~~~
// 屬性 'json' 在類型 'Promise<Response>'上不存在.
//
// Did you forget to use 'await'?
}
這裏的目的是就算用戶沒意識到要await,起碼這些消息能給出一些提示。
除了改進Promise相關的錯誤消息外,我們現在還在某些情況下提供了快速修復。
有關更多詳細信息,請參閱原始問題及相關鏈接。
對標識符的Unicode支持改進
當發射到ES2015及更高版本的目標時,TypeScript 3.6對標識符的Unicode字符提供了更好的支持。
const 𝓱𝓮𝓵𝓵𝓸 = "world"; // 以前不允許, 現在對 '--target es2015' 允許
SystemJS中的import.meta支持
當module目標設置爲system時,TypeScript 3.6支持將import.meta轉換爲context.meta。
// 這個模塊:
console.log(import.meta.url)
// 被改成:
System.register([], function (exports, context) {
return {
setters: [],
execute: function () {
console.log(context.meta.url);
}
};
});
在環境上下文中允許get和set訪問器
以前的TypeScript版本不允許在環境上下文中get和set訪問器(例如在declare-d類中,或者在.d.ts文件中)。理由是對於這些屬性的讀寫而言訪問器與屬性沒有區別;但是因爲ECMAScript的類字段提案可能與現有TypeScript版本中的行爲不同,我們意識到我們需要一種方法來對接這種行爲差異,以便在子類中提供適當的錯誤。
因此,用戶現在可以在TypeScript 3.6的環境上下文中編寫getter和setter。
declare class Foo {
// 在 3.6+ 版本中允許.
get x(): number;
set x(val: number): void;
}
在TypeScript 3.7中編譯器也將利用此功能,以便生成的.d.ts文件也發射get/set訪問器。
環境類和函數可以合併
在以前版本的TypeScript中,任何情況下合併類和函數都是錯誤的。現在環境類和函數(具有declare修飾符或在.d.ts文件中的類/函數)可以合併了。這意味着現在你可以編寫以下代碼:
export declare function Point2D(x: number, y: number): Point2D;
export declare class Point2D {
x: number;
y: number;
constructor(x: number, y: number);
}
這樣就用不着再寫成:
export interface Point2D {
x: number;
y: number;
}
export declare var Point2D: {
(x: number, y: number): Point2D;
new (x: number, y: number): Point2D;
}
這樣做的一個優點是可以很容易地表達可調用的構造函數模式,同時還允許名稱空間與這些聲明合併(因爲var聲明不能與namespace合併)。
在TypeScript 3.7中,編譯器也將利用此功能,以便從.js文件生成的.d.ts文件可以正確捕獲類函數的可調用性和可構造性。
更多詳細信息請參閱GitHub上的原始PR。
爲–build和–incremental提供API支持
TypeScript 3.0開始支持引用其他項目,並使用–build標誌以增量方式構建它們。之後TypeScript 3.4引入了–incremental標誌來保存之前編譯的相關信息,這樣就可以只重建特定文件了。這些標誌可以幫助用戶更快、靈活地構建項目。可惜這些標誌無法用於Gulp和Webpack這樣的第三方構建工具。TypeScript 3.6現在公開了兩組API來處理項目引用和增量程序構建。
創建–incremental構建時用戶可以利用createIncrementalProgram和createIncrementalCompilerHost API。用戶還可以使用新公開的readBuilderProgram函數從這個API生成的.tsbuildinfo文件中重新保存舊程序實例,該函數僅用於創建新程序(你無法修改返回的實例,它僅用於其他create*Program函數中的oldProgram參數)。
針對項目引用方面,新版引入了一個新的createSolutionBuilder函數,它返回一個新類型SolutionBuilder的實例。
有關這些API的詳細信息可參閱資料。
新的TypeScript Playground
應用戶呼籲,新版TypeScript Playground做了大幅改進,引入了許多全新功能。新版Playground基本上是社區流行的Artem Tyurin的TypeScript Playground的一個fork。我們在這裏非常感謝Artem提供的幫助!
新版Playground提供了許多新選項,包括:
- target選項(允許用戶從es5切換到es3、es2015、esnext等)
- 所有嚴格標誌(包括strict)
- 支持純JavaScript文件(使用allowJS和可選的checkJs)
這些選項在共享Playground樣本時也能使用,讓用戶可以更可靠地共享示例,無需告訴收件人“哦,別忘了打開noImplicitAny選項!”。
在不久的將來,我們將改進Playground樣本功能、添加JSX支持、改進自動類型獲取等,讓用戶使用Playground時如同在使用自己的編輯器一樣。
我們歡迎大家在GitHub上提交反饋和請求!
編輯功能
分號感知代碼編輯
Visual Studio和Visual Studio Code等編輯器可以自動應用快速修復、重構、自動從其他模塊導入值等轉換。這些轉換由TypeScript提供支持,而舊版本的TypeScript會無條件地在每個語句的末尾添加分號;但很多用戶不喜歡這種風格,不希望編輯器自動插入分號。
TypeScript現在變得非常聰明,可以在應用這些編輯時檢測你的文件是否使用分號。如果你的文件不怎麼用分號的話TypeScript也不會添加分號。
更多詳細信息請參閱資料。
更智能的自動導入
JavaScript有許多不同的模塊語法或約定:ECMAScript標準是一種、Node支持的一種(CommonJS)、AMD一種、System.js又是一種,還有更多!在大多數情況下TypeScript默認使用ECMAScript模塊語法來自動導入,遇到有些使用不同編譯器設置的TypeScript項目就不怎麼合適,在使用純JavaScript和require調用的Node項目中也不搭配。
TypeScript 3.6變得更聰明瞭一些,可以先查看你現有的導入後再決定怎樣自動導入其他模塊。你可以在此處查看更多信息。
重大更新
命名爲“constructor”的類成員現在是構造函數
根據ECMAScript規範,使用名爲constructor的方法的類聲明現在是構造函數,無論它們是使用標識符名稱還是字符串名稱聲明都是如此。
class C {
"constructor"() {
console.log("我是構造函數.");
}
}
注意有一個例外,就是計算屬性的名稱等於“constructor”的情況。
class D {
"constructor" {
console.log("我不是一個構造函數 - 只是一個方法");
}
}
DOM更新
新版lib.dom.d.ts中的許多聲明已被刪除或更改。具體包括(但不限於)以下內容:
- 全局window不再定義爲類型Window——而是將其定義爲類型Window&typeof globalThis。在某些情況下,最好將其類型稱爲typeofwindow。
- GlobalFetch已經移除了,取而代之的是WindowOrWorkerGlobalScope。
- Navigator上的某些非標準屬性消失了。
- experimental-webgl上下文移除了。替代品是webgl或webgl2。
JSDoc註釋不再合併
在JavaScript文件中,TypeScript只會在JSDoc註釋之前確定聲明的類型。
/**
* @param {string} arg
*/
/**
* oh, hi, were you trying to type something?
*/
function whoWritesFunctionsLikeThis(arg) {
// 'arg' has type 'any'
}
關鍵字不能包含轉義序列
以前關鍵字允許包含轉義序列。TypeScript 3.6中就不行了。
while (true) {
\u0063ontinue;
// ~~~~~~~~~~~~~
// error! Keywords cannot contain escape characters.
}
未來計劃
想要了解官方團隊未來要開展的工作,請查看今年7月至12月的半年路線圖計劃。
我們希望這個新版本能繼續改善你的開發體驗。你有任何建議或遇到任何問題我們都很感興趣,歡迎大家在GitHub上提交反饋。
英文原文:https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/