As Const:一個被低估的 TypeScript 特性
你有沒有感覺 TypeScript中可能有一些被低估但卻非常有用的工具,你并沒有充分利用?的確有,今天我們要重點介紹一個:as const。它雖然沉默卻強大,而且非常有力,是一個被低估的功能,但它的力量卻強大無比。
理解 'as const'
以下是一個沒有 as const 的代碼片段:
const menu = {
home: '/home',
about: '/about',
contact: '/contact'
};
這個TypeScript的世界里,這個變量可以隨心所欲地變化和改變。聽起來很靈活,,但是這里有個陷阱。
請考慮這個 go to root 方法:
function goTo(route: 'home' | 'about' | 'contact') {
// some implementation
}
注意到了嗎?如果你在 menu 對象中添加了另一條路由,你也需要更新函數(shù) goTo 。這就是冗余,導(dǎo)致同一類型有多個真實來源。
as const 可以為我們解決這個問題。
TypeScript的期望與現(xiàn)實
當你在使用TypeScript時,有時你所期待的和實際發(fā)生的情況會痛苦地產(chǎn)生巨大的分歧。當我們試圖從現(xiàn)有的類型中創(chuàng)建一個新的類型時,這種分歧變得非常明顯。
這里有個例子。假設(shè)你有一個對象,你期望TypeScript只考慮這個對象的屬性。但是,意外的是!TypeScript只把它當作一個字符串來考慮。
let route: keyof typeof menu;
route = 'store'; // No error
在這種情況下,TypeScript認為 route 是一個可能會發(fā)生變化的字符串。但是,我們希望基于屬性的固定類型。我們的期望和現(xiàn)實并未對齊。
解決方案:'as const'
我們剛剛經(jīng)歷的那種痛苦的分歧,就是 as const 試圖解決的問題。通過將易變屬性改為不變屬性, as const 使我們的期望與現(xiàn)實保持一致。
const menu = {
home: '/home',
about: '/about',
contact: '/contact'
} as const;
通過這個簡單的改變,我們的對象屬性變成了只讀。我們看看它如何影響我們之前的問題:
let route: keyof typeof menu;
route = 'store'; // Error. Exactly what we wanted!
通過使用 as const 使對象變?yōu)椴豢勺?,TypeScript 現(xiàn)在明白 route 應(yīng)該只允許提供的鍵?,F(xiàn)在,我們得到了我們想要的確切結(jié)果:當我們試圖設(shè)置無效值時,會出現(xiàn)類型錯誤。
與 object.freeze 的比較
你可能對JavaScript方法 Object.freeze() 有所了解。 Object.freeze() 和 as const 都可以使對象只讀,但它們之間存在著關(guān)鍵的差異。
我們快速了解一下他們的能力:
const menuFrozen = Object.freeze({
home: '/home',
about: '/about',
contact: '/contact'
});
const menuConst = {
home: '/home',
about: '/about',
contact: '/contact'
} as const;
以及一些結(jié)果:
// 這不會改變?nèi)魏螙|西,home 仍然是'/home'
menuFrozen.home = '/newHome';
// 編譯時錯誤
menuConst.home = '/newHome';
通過 Object.freeze() ,我們擁有了一個運行時概念,可以防止JavaScript對象的更改。然而, Object.freeze() 并不影響 TypeScript 的類型推斷。
另一方面,有了 as const ,TypeScript在編譯時將對象視為不可變的,使你的類型檢查更為嚴格,這有助于捕捉更多可能的錯誤。
因此,雖然 as const 和 Object.freeze() 在表面上看起來可能相似,但它們服務(wù)于不同的目的。 as const 在類型檢查上更為強大,而 Object.freeze() 只在運行時強制實施不變性。
一個配合 'as const' 的更清潔的 'go to root' 函數(shù)
我們使用 as const 重構(gòu) goTo 函數(shù):
function goTo(route: keyof typeof menu) {
// some implementation
}
就這樣,as const 使我們免于重復(fù)信息的愚蠢操作。我們可以從我們創(chuàng)建的對象中推斷出一個類型。這樣是不是更簡潔了?
使用 'as const' 提取對象值
我們使用 as const 提取我對象值,顛覆TypeScript的規(guī)則,獲取我們需要的所有詳細信息,以編寫強大且無bug的代碼。這只需要一點類型魔法。
以下是一個示例:
type Routes = typeof menu[keyof typeof menu];
// Routes is now equal to '/home' | '/about' | '/contact'