在TypeScript中迭代對(duì)象鍵Object.keys不起作用的原因和解決方案
原文鏈接:https://www.totaltypescript.com/iterate-over-object-keys-in-typescript
在TypeScript中迭代對(duì)象鍵object keys
可能是一場(chǎng)噩夢(mèng)。以下是我所知道的所有解決方案。
快速解釋 - 省流
使用 Object.keys
迭代不起作用,是因?yàn)?Object.keys
返回一個(gè)字符串?dāng)?shù)組,而不是所有鍵的并集。這是設(shè)計(jì)出來的,不會(huì)改變。
function printUser(user: User) { Object.keys(user).forEach((key) => { // Doesn't work! console.log(user[key]); // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'User'. // No index signature with a parameter of type 'string' was found on type 'User'. }); }
解決方法:
- 可以在正確的位置投射到
keyof typeof
使其工作:
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user); keys.forEach((key) => { console.log(user[key as keyof typeof user]); });
- 自定義類型謂詞也可以通過內(nèi)聯(lián)縮小類型來工作。
function isKey<T extends object>( x: T, k: PropertyKey ): k is keyof T { return k in x; } keys.forEach((key) => { if (isKey(user, key)) { console.log(user[key]); } });
更長(zhǎng)的解釋
這里有一個(gè)問題:使用Object.keys似乎并不像期望的那樣工作。這是因?yàn)樗鼪]有返回你需要的類型。它不是一個(gè)包含所有鍵的類型,而是將其擴(kuò)展為一個(gè)字符串?dāng)?shù)組。
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user); // const keys: string[]
這意味著你不能使用鍵來訪問對(duì)象上的值:
const nameKey = keys[0]; // const nameKey: string user[nameKey]; // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'. // No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.
TypeScript在這里返回字符串?dāng)?shù)組有一個(gè)很好的原因。TypeScript對(duì)象類型是開放式的。
在很多情況下,TS不能保證 Object.keys
返回的鍵實(shí)際上在對(duì)象上,因此將它們擴(kuò)展為字符串是唯一合理的解決方案。查看此問題以了解更多詳細(xì)信息。
在For循環(huán)中
如果你嘗試做一個(gè)For...在循環(huán)中,你也會(huì)失敗的。這是出于同樣的原因——key被推斷為字符串,就像 Object.keys
一樣。
function printUser(user: User) { for (const key in user) { console.log(user[key]); // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'User'. // No index signature with a parameter of type 'string' was found on type 'User'. } }
但在許多情況下,你實(shí)際上能夠確認(rèn)對(duì)象是什么樣的。所以,你是怎么做的?
解決方案1:轉(zhuǎn)換為 keyof typeof
第一個(gè)選項(xiàng)是使用 keyof typeof
將鍵轉(zhuǎn)換為更特定的類型。在下面的示例中,我們將 Object.keys
的結(jié)果轉(zhuǎn)換為包含這些鍵的數(shù)組。
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user) as Array<keyof typeof user>; keys.forEach((key) => { // (parameter) key: "name" | "age" // No more error! console.log(user[key]); });
我們也可以在索引到對(duì)象的時(shí)候這樣做。在這里, key
仍然是一個(gè)字符串類型-但在我們索引到user時(shí),我們將其轉(zhuǎn)換為 keyof typeof user
。
const keys = Object.keys(user); keys.forEach((key) => { // (parameter) key: string console.log(user[key as keyof typeof user]); });
然而,以任何形式使用 as
通常都是不安全的,這也不例外。
const user = { name: "Daniel", age: 26, }; const nonExistentKey = "id" as keyof typeof user; // const nonExistentKey: "name" | "age" // No error! const value = user[nonExistentKey];
對(duì)于這種情況, as
是一個(gè)相當(dāng)強(qiáng)大的工具-正如你所看到的,它讓我們對(duì)TypeScript撒謊。
解決方案2:類型謂詞 Type Predicates
讓我們來看看一些更聰明,可能更安全的解決方案。類型謂詞怎么樣?通過使用 isKey
helper,我們可以在索引到對(duì)象之前檢查鍵是否確實(shí)在對(duì)象上。我們通過在 isKey
的返回類型中使用 is
語法來正確推斷TypeScript。
function isKey<T extends object>( x: T, k: PropertyKey ): k is keyof T { return k in x; } keys.forEach((key) => { if (isKey(user, key)) { console.log(user[key]); } });
這個(gè)令人敬畏的解決方案來自Stefan Baumgartner關(guān)于這個(gè)主題的偉大博客文章https://fettblog.eu/typescript-iterating-over-objects/
解決方案3:泛型函數(shù)
讓我們來看看一個(gè)稍微奇怪的解決方案。在泛型函數(shù)中,使用 in
操作符將類型縮小到鍵。我真的不知道為什么這里可以這樣用,而非泛型版本不行。
function printEachKey<T extends object>(obj: T) { for (const key in obj) { console.log(obj[key]); // const key: Extract<keyof T, string> } } // Each key gets printed! printEachKey({ name: "Daniel", age: 26, });
解決方案4:在函數(shù)中包裝Object.keys
另一種解決方案是將 Object.keys
包裝在一個(gè)返回強(qiáng)制轉(zhuǎn)換類型的函數(shù)中。
const objectKeys = <T extends object>(obj: T) => { return Object.keys(obj) as Array<keyof T>; }; const keys = objectKeys({ name: "Daniel", age: 26, }); console.log(keys); // const keys: ("name" | "age")[]
這可能是最容易被濫用的解決方案——將轉(zhuǎn)換操作隱藏在函數(shù)內(nèi)部的方法會(huì)使其更具吸引力,但可能導(dǎo)致人們?cè)谑褂脮r(shí)不加思考。
結(jié)論
我的首選解決方案?通常情況下,類型轉(zhuǎn)換非常完美地完成了工作。它簡(jiǎn)單易懂,并且通常足夠安全。
但是如果你喜歡類型謂詞或通用解決方案的外觀,請(qǐng)盡管使用。isKey函數(shù)看起來很有用,我會(huì)在下一個(gè)項(xiàng)目中借用它。
以上就是在TypeScript中迭代對(duì)象鍵Object.keys不起作用的原因和解決方案的詳細(xì)內(nèi)容,更多關(guān)于TypeScript Object.keys不起作用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序使用request網(wǎng)絡(luò)請(qǐng)求操作實(shí)例
這篇文章主要介紹了微信小程序使用request網(wǎng)絡(luò)請(qǐng)求操作,結(jié)合實(shí)例形式分析了wx.request(object)網(wǎng)絡(luò)請(qǐng)求操作的具體使用技巧,需要的朋友可以參考下2017-12-12JavaScript實(shí)現(xiàn)留言板添加刪除留言
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)留言板添加刪除留言,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面方法詳解
Uniapp是一款基于Vue.js框架的跨平臺(tái)開發(fā)工具,支持在一套代碼中開發(fā)出運(yùn)行于各大平臺(tái)的應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面的相關(guān)資料,需要的朋友可以參考下2023-09-09js設(shè)置隨機(jī)切換背景圖片的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄猨s設(shè)置隨機(jī)切換背景圖片的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11用Javascript數(shù)組處理多個(gè)字符串的連接問題
小技巧 用Javascript數(shù)組處理多個(gè)字符串的連接問題,大家可以參考下。2009-08-08js父窗口關(guān)閉時(shí)子窗口隨之關(guān)閉完美解決方案
admin注銷的時(shí)候,或者main.html關(guān)閉的時(shí)候,如何讓打開的所有新窗口一起關(guān)閉,下面有個(gè)不錯(cuò)的解決方案,大家可以參考下2014-04-04用函數(shù)模板,寫一個(gè)簡(jiǎn)單高效的 JSON 查詢器的方法介紹
本篇文章小編將為大家介紹,用函數(shù)模板,寫一個(gè)簡(jiǎn)單高效的 JSON 查詢器的方法介紹,需要的朋友可以參考一下2013-04-04js或者jquery判斷圖片是否加載完成實(shí)現(xiàn)代碼
需要獲得圖片的寬度和高度,有些js或者jquery代碼在還沒有加載完圖片時(shí)就執(zhí)行了,這個(gè)問題該怎么解決呢?接下來分別介紹下js與jquery提供的方法2013-03-03