就在上月微軟發(fā)布了 TypeScript 4.8 新版本,一起來看看都有哪些新的功能:
-- 改進(jìn)的交叉類型、聯(lián)合兼容性和類型收窄[1]
-- 改進(jìn)了對(duì)infer模板字符串類型中的類型的推理[2]
-- build --watch --incremental性能改進(jìn)[3]
-- 比較對(duì)象和數(shù)組文字時(shí)的錯(cuò)誤[4]
-- 從綁定模式改進(jìn)推理[5]
-- 文件監(jiān)視修復(fù)(尤其是跨 git checkouts)[6]
-- Find-All-References 性能改進(jìn)[7]
-- 從自動(dòng)導(dǎo)入中排除特定文件[8]
-- 正確性修復(fù)和重大更改[9]
如果您還不熟悉 TypeScript,可以在官網(wǎng)了解有關(guān) TypeScript 的更多信息[10]。它是一種基于 JavaScript 并為類型添加語(yǔ)法的語(yǔ)言。這些類型讓您可以將您的期望和假設(shè)放入代碼中,然后可以通過 TypeScript 類型檢查器檢查這些假設(shè)。
這種檢查可以幫助避免拼寫錯(cuò)誤、調(diào)用未初始化的值、混淆函數(shù)的參數(shù)等等。類型不僅僅是檢查,它還用于為您提供強(qiáng)大的 TypeScript 和 JavaScript 編輯體驗(yàn),支持代碼完成、轉(zhuǎn)到定義、重命名等。我們所熟悉的 Visual Studio 就是完全使用 TypeScript 開發(fā)的!
要開始使用 TypeScript,可以通過 NuGet[11]獲取它,或者通過以下命令使用 npm:
npm install -D typescript
還可以通過以下方式獲得編輯器支持
下載 Visual Studio 2022/2019[12]
安裝 Visual Studio Code 的 Insiders 版本[13]或按照說明使用較新版本的 TypeScript[14]
使用 Sublime Text 3 的包控制[15]。
自 Beta 版和 RC 版以來有什么新變化?
自 測(cè)試版發(fā)布[16] 以來,其穩(wěn)定版現(xiàn)在支持排除哪些文件被考慮在自動(dòng)導(dǎo)入[17]中。測(cè)試版發(fā)布帖子也沒有記錄類型簽名中未使用的解構(gòu)別名的突破。此外,Beta 版和 RC 帖子都沒有記錄關(guān)于在 TypeScript 語(yǔ)法樹上放置裝飾器的 API 中斷。這些中斷現(xiàn)在在正確性修復(fù)和重大更改部分中進(jìn)行了詳細(xì)說明[18]
改進(jìn)交叉類型、聯(lián)合兼容性和收窄
TypeScript 4.8 版本對(duì) --strictNullChecks 進(jìn)行了改進(jìn)增強(qiáng),這些更改會(huì)聯(lián)合類型與交叉類型的工作方式,并在類型收縮時(shí)加以利用。
例如,人們普遍認(rèn)為 unknown 和 {} | null | undefined 很接近,因?yàn)樗邮躰ull, undefined 和任何其他類型。TypeScript 現(xiàn)在可以識(shí)別這一點(diǎn),并允許從to賦值。
function f(x: unknown, y: {} | null | undefined) {
x = y; // always worked
y = x; // used to error, now works
}
另一個(gè)變化是,{} 與任何其他對(duì)象類型相交會(huì)直接簡(jiǎn)化為該對(duì)象類型。這意味著能夠像下面這樣重寫 NonNullable ,因?yàn)?{} & null 和 {} & undefined 只是被扔掉了。
- type NonNullable<T> = T extends null | undefined ? never : T;
+ type NonNullable<T> = T & {};
這個(gè)改進(jìn)可以減少和分配像這樣的交集類型,而條件類型目前不能。
function foo<T>(x: NonNullable<T>, y: NonNullable<NonNullable<T>>) {
x = y; // always worked
y = x; // used to error, now works
}
這些更改還使我們能夠在控制流分析和類型縮小方面進(jìn)行明智的改進(jìn)。
function narrowUnknownishUnion(x: {} | null | undefined) {
if (x) {
x; // {}
}
else {
x; // {} | null | undefined
}
}
function narrowUnknown(x: unknown) {
if (x) {
x; // used to be 'unknown', now '{}'
}
else {
x; // unknown
}
}
我們現(xiàn)在可以在沒有任何類型斷言的情況下定義以下函數(shù)。
function throwIfNullable<T>(value: T): NonNullable<T> {
if (value === undefined || value === null) {
throw Error("Nullable value!");
}
// Used to fail because 'T' was not assignable to 'NonNullable<T>'.
// Now narrows to 'T & {}' and succeeds because that's just 'NonNullable<T>'.
return value;
}
有關(guān)這些改進(jìn)的更多細(xì)節(jié),可以在此處閱讀更多信息[19]。
改進(jìn)了對(duì)infer模板字符串類型中的類型的推理
TypeScript 最近引入了一種向條件類型中的類型變量添加extends約束的方法。infer
// Grabs the first element of a tuple if it's assignable to 'number',
// and returns 'never' if it can't find one.
type TryGetNumberIfFirst<T> =
T extends [infer U extends number, ...unknown[]] ? U : never;
如果這些infer類型出現(xiàn)在模板字符串類型中并且被限制為原始類型,TypeScript 現(xiàn)在將嘗試解析出文字類型。
// SomeNum used to be 'number'; now it's '100'.
type SomeNum = "100" extends `${infer U extends number}` ? U : never;
// SomeBigInt used to be 'bigint'; now it's '100n'.
type SomeBigInt = "100" extends `${infer U extends bigint}` ? U : never;
// SomeBool used to be 'boolean'; now it's 'true'.
type SomeBool = "true" extends `${infer U extends boolean}` ? U : never;
這現(xiàn)在可以更好地傳達(dá)庫(kù)在運(yùn)行時(shí)將做什么,并提供更精確的類型。
可以在此處查看有關(guān)此功能的更多信息[20]。
–build, --watch, --incremental 性能改進(jìn)
TypeScript 4.8 引入了一些優(yōu)化,這些優(yōu)化現(xiàn)在能夠避免在模式的無(wú)操作更改期間花費(fèi)時(shí)間更新時(shí)間戳,這使得重建速度更快,并避免與可能正在監(jiān)視 TypeScript 輸出的其他構(gòu)建工具混淆。
微軟生成在一個(gè)相當(dāng)大的內(nèi)部代碼庫(kù)中,他們已經(jīng)看到許多簡(jiǎn)單的常見操作的時(shí)間減少了 10%-25%,在無(wú)變化的情況下減少了大約 40% 的時(shí)間,而在 TypeScript 代碼庫(kù)上也看到了類似的結(jié)果。
可以在 GitHub 上查看更改以及性能結(jié)果[21]。
比較對(duì)象和數(shù)組文字時(shí)的錯(cuò)誤
在 JavaScript 中,通常 或= 只能在對(duì)象(以及數(shù)組)之間檢查兩個(gè)引用是否指向相同的值,并不能判斷值相等,團(tuán)隊(duì)認(rèn)為這有可能導(dǎo)致生產(chǎn)代碼中的一些錯(cuò)誤。TypeScript 現(xiàn)在不允許像下面這樣的代碼。
if (peopleAtHome === []) {
// ~~~~~~~~~~~~~~~~~~~
// This condition will always return 'false' since JavaScript compares objects by reference, not value.
console.log("here's where I lie, broken inside. </3")
adoptAnimals();
}
可以在此處查看所涉及的更改[22]。
從綁定模式改進(jìn)推導(dǎo)
在某些情況下,TypeScript 會(huì)從綁定模式中選擇一個(gè)類型以進(jìn)行更好的推導(dǎo)。
declare function chooseRandomly<T>(x: T, y: T): T;
let [a, b, c] = chooseRandomly([42, true, "hi!"], [0, false, "bye!"]);
// ^ ^ ^
// | | |
// | | string
// | |
// | boolean
// |
// number
了解更多信息,可以查看 GitHub 上的更改。[23]
文件監(jiān)視修復(fù)(尤其是跨 git checkout)
TypeScript 在 watch 模式和編輯器場(chǎng)景下很難對(duì)某些文件進(jìn)行修改,有時(shí)癥狀是陳舊的或不準(zhǔn)確的錯(cuò)誤,可能需要重新啟動(dòng) tsc 或 VS Code。如果在Unix系統(tǒng)中使用vim保存文件或在git中交換分支的話,這種情況經(jīng)常發(fā)生。
這是由于對(duì) Node.js 如何跨文件系統(tǒng)處理重命名事件的假設(shè)造成的。Linux 和 macOS
使用的文件系統(tǒng)使用inode[24],并且Node.js 會(huì)將文件觀察程序附加到 inode 而不是文件路徑[25]。因此,當(dāng)
Node.js 返回一個(gè)觀察者對(duì)象[26]時(shí),它是正在觀察路徑還是在索引節(jié)點(diǎn),得取決于平臺(tái)和文件系統(tǒng)。
如果 TypeScript 檢測(cè)到磁盤上仍然存在路徑,它會(huì)嘗試重用相同的觀察者對(duì)象,這是為了提高效率,但也就是它導(dǎo)致了問題所在,因?yàn)榧词乖撀窂缴先匀淮嬖谝粋€(gè)文件,也可能已經(jīng)創(chuàng)建了一個(gè)不同的文件,并且該文件將具有不同的 inode。
因此,TypeScript 最終會(huì)重用 watcher 對(duì)象,而不是在原始位置安裝新的 watcher,并在可能完全不相關(guān)的文件中監(jiān)視更改。因此 TypeScript 4.8 現(xiàn)在可以在 inode 系統(tǒng)上處理這些情況,并正確安裝新的觀察程序并修復(fù)此問題。
可以在此處查看有關(guān)文件監(jiān)視的特定修復(fù)[27]。
Find-All-References 性能改進(jìn)
在編輯器中運(yùn)行 find-all-references 時(shí),TypeScript 現(xiàn)在能夠更智能地聚合引用。這將 TypeScript 在其自己的代碼庫(kù)中搜索廣泛使用的標(biāo)識(shí)符所花費(fèi)的時(shí)間減少了約 20%。
可以在此處閱讀有關(guān)改進(jìn)的更多信息[28]。
從自動(dòng)導(dǎo)入中排除特定文件
TypeScript 4.8引入了一個(gè)編輯器偏好,用于從自動(dòng)導(dǎo)入中排除文件。在Visual Studio Code中,文件名或glob可以添加在Settings UI中的“自動(dòng)導(dǎo)入文件排除模式”下,或者在.vscode/ Settings中。json文件:
{
// Note that `javascript.preferences.autoImportFileExcludePatterns` can be specified for JavaScript too.
"typescript.preferences.autoImportFileExcludePatterns": [
"**/node_modules/@types/node"
]
}
這在無(wú)法避免編譯中包含某些模塊或庫(kù),但又不太希望它們導(dǎo)入的情況下很有用。這些模塊可能有很多可能會(huì)污染自動(dòng)導(dǎo)入列表并使其更難自動(dòng)的導(dǎo)出,而此選項(xiàng)可以在這些情況下提供幫助。
您可以在此處查看有關(guān)實(shí)施的更多細(xì)節(jié)[29]。
正確性修復(fù)和重大更改
由于類型系統(tǒng)更改的性質(zhì),可以進(jìn)行的更改很少不會(huì)影響某些代碼;但是,有一些更改更有可能需要調(diào)整現(xiàn)有代碼。
lib.d.ts更新
雖然TypeScript努力避免大的中斷,但即使是對(duì)內(nèi)置庫(kù)的小改動(dòng)也會(huì)導(dǎo)致問題。我們不認(rèn)為DOM和lib.d.ts更新會(huì)導(dǎo)致重大中斷,但一個(gè)值得注意的變化是Errors上的cause屬性現(xiàn)在的類型為unknown,而不是Error。
不受約束的泛型不再可分配給{}
在 TypeScript 4.8 中,對(duì)于啟用了 strictNullChecks 的項(xiàng)目,當(dāng)不受約束的類型參數(shù)被用在 null 或 undefined 不是合法值的位置時(shí),TypeScript現(xiàn)在會(huì)正確地發(fā)出錯(cuò)誤。這將包括任何需要 {}、object 或具有所有可選屬性的對(duì)象類型。
一個(gè)簡(jiǎn)單的例子:
// Accepts any non-null non-undefined value
function bar(value: {}) {
Object.keys(value); // This call throws on null/undefined at runtime.
}
// Unconstrained type parameter T...
function foo<T>(x: T) {
bar(x); // Used to be allowed, now is an error in 4.8.
// ~
// error: Argument of type 'T' is not assignable to parameter of type '{}'.
}
foo(undefined);
如上所示,這樣的代碼有一個(gè)潛在的錯(cuò)誤 null undefined 這些值可以通過這些不受約束的類型參數(shù)間接傳遞給不應(yīng)該觀察這些值的代碼。
此行為也將在類型位置中可見,一個(gè)例子是:
interface Foo<T> {
x: Bar<T>;
}
interface Bar<T extends {}> { }
不想處理的現(xiàn)有代碼null可以u(píng)ndefined通過傳播適當(dāng)?shù)募s束來修復(fù)。
- function foo<T>(x: T) {
+ function foo<T extends {}>(x: T) {
另一種解決方法是在運(yùn)行時(shí)檢查null和undefined。
function foo<T>(x: T) {
+ if (x !== null && x !== undefined) {
bar(x);
+ }
}
如果知道由于某種原因,通用值不能是nullor undefined,可以只使用非空斷言。
function foo<T>(x: T) {
- bar(x);
+ bar(x!);
}
有關(guān)更多信息,可以查看引入此內(nèi)容的更改[30]以及關(guān)于無(wú)約束泛型現(xiàn)在如何工作的具體討論問題[31]。
裝飾器放置在modifiersTypeScript 的語(yǔ)法樹上
TypeScript 公開了一個(gè)名為 ModifierLike 的新類型別名,它是一個(gè) Modifier 或 Decorator。
export type ModifierLike = Modifier | Decorator;
有關(guān)更多信息,請(qǐng)參閱周圍的更改
樹節(jié)點(diǎn)的重組[32]
棄用[33]
暴露謂詞函數(shù)[34]
無(wú)法在 JavaScript 文件中導(dǎo)入/導(dǎo)出類型
TypeScript 以前允許 JavaScript 文件在 import 和 export 語(yǔ)句中導(dǎo)入和導(dǎo)出使用類型聲明但沒有值的實(shí)體,這種行為是不正確的,因?yàn)樵?ECMAScript 模塊下,不存在的值的命名導(dǎo)入和導(dǎo)出將導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。
當(dāng)一個(gè) JavaScript 文件在 ——checkJs 下或通過 // @ts-check 注釋進(jìn)行類型檢查時(shí),TypeScript現(xiàn)在會(huì)發(fā)出一個(gè)錯(cuò)誤。
// @ts-check
// Will fail at runtime because 'SomeType' is not a value.
import { someValue, SomeType } from "some-module";
/**
* @type {SomeType}
*/
export const myValue = someValue;
/**
* @typedef {string | number} MyType
*/
// Will fail at runtime because 'MyType' is not a value.
export { MyType as MyExportedType };
要從另一個(gè)模塊引用類型,可以直接限制導(dǎo)入。
- import { someValue, SomeType } from "some-module";
+ import { someValue } from "some-module";
/**
- * @type {SomeType}
+ * @type {import("some-module").SomeType}
*/
export const myValue = someValue;
要導(dǎo)出類型,只需/** @typedef */在 JSDoc 中使用注釋即可。@typedef注釋已經(jīng)自動(dòng)從其包含的模塊中導(dǎo)出類型。
/**
* @typedef {string | number} MyType
*/
+ /**
+ * @typedef {MyType} MyExportedType
+ */
- export { MyType as MyExportedType };
綁定模式中未使用的重命名現(xiàn)在是類型簽名中的錯(cuò)誤
TypeScript 的類型注釋語(yǔ)法通??雌饋砜梢栽诮鈽?gòu)值時(shí)使用。例如,采用以下函數(shù)。
declare function makePerson({ name: string, age: number }): Person;
讀到這個(gè)簽名時(shí),您可能會(huì)認(rèn)為 makePerson 顯然接受了一個(gè)對(duì)象,該對(duì)象的 name 屬性為類型字符串,age 屬性為類型數(shù)字。然而 makePerson 雖然確實(shí)表示它將接受一個(gè)具有名稱和年齡屬性的對(duì)象,卻沒有為它們指定類型,它只是說它將名稱和年齡分別重命名為字符串和數(shù)字。
在純類型構(gòu)造中,編寫這樣的代碼是沒有用的,而且通常是錯(cuò)誤的,因?yàn)殚_發(fā)人員通常認(rèn)為他們正在編寫類型注釋。
TypeScript 4.8 使這些成為錯(cuò)誤,除非稍后在簽名中引用它們。編寫上述簽名的正確方法如下:文章來源:http://www.zghlxwxcb.cn/news/detail-462637.html
declare function makePerson(options: { name: string, age: number }): Person;
// or
declare function makePerson({ name, age }: { name: string, age: number }): Person;
。文章來源地址http://www.zghlxwxcb.cn/news/detail-462637.html
到了這里,關(guān)于微軟宣布 TypeScript 4.8 正式發(fā)布的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!