在TypeScript中迭代對象鍵Object.keys不起作用的原因和解決方案
原文鏈接:https://www.totaltypescript.com/iterate-over-object-keys-in-typescript
在TypeScript中迭代對象鍵object keys
可能是一場噩夢。以下是我所知道的所有解決方案。
快速解釋 - 省流
使用 Object.keys
迭代不起作用,是因為 Object.keys
返回一個字符串數(shù)組,而不是所有鍵的并集。這是設計出來的,不會改變。
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]); } });
更長的解釋
這里有一個問題:使用Object.keys似乎并不像期望的那樣工作。這是因為它沒有返回你需要的類型。它不是一個包含所有鍵的類型,而是將其擴展為一個字符串數(shù)組。
const user = { name: "Daniel", age: 26, }; const keys = Object.keys(user); // const keys: string[]
這意味著你不能使用鍵來訪問對象上的值:
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在這里返回字符串數(shù)組有一個很好的原因。TypeScript對象類型是開放式的。
在很多情況下,TS不能保證 Object.keys
返回的鍵實際上在對象上,因此將它們擴展為字符串是唯一合理的解決方案。查看此問題以了解更多詳細信息。
在For循環(huán)中
如果你嘗試做一個For...在循環(huán)中,你也會失敗的。這是出于同樣的原因——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'. } }
但在許多情況下,你實際上能夠確認對象是什么樣的。所以,你是怎么做的?
解決方案1:轉(zhuǎn)換為 keyof typeof
第一個選項是使用 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]); });
我們也可以在索引到對象的時候這樣做。在這里, key
仍然是一個字符串類型-但在我們索引到user時,我們將其轉(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];
對于這種情況, as
是一個相當強大的工具-正如你所看到的,它讓我們對TypeScript撒謊。
解決方案2:類型謂詞 Type Predicates
讓我們來看看一些更聰明,可能更安全的解決方案。類型謂詞怎么樣?通過使用 isKey
helper,我們可以在索引到對象之前檢查鍵是否確實在對象上。我們通過在 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]); } });
這個令人敬畏的解決方案來自Stefan Baumgartner關(guān)于這個主題的偉大博客文章https://fettblog.eu/typescript-iterating-over-objects/
解決方案3:泛型函數(shù)
讓我們來看看一個稍微奇怪的解決方案。在泛型函數(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
包裝在一個返回強制轉(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)部的方法會使其更具吸引力,但可能導致人們在使用時不加思考。
結(jié)論
我的首選解決方案?通常情況下,類型轉(zhuǎn)換非常完美地完成了工作。它簡單易懂,并且通常足夠安全。
但是如果你喜歡類型謂詞或通用解決方案的外觀,請盡管使用。isKey函數(shù)看起來很有用,我會在下一個項目中借用它。
以上就是在TypeScript中迭代對象鍵Object.keys不起作用的原因和解決方案的詳細內(nèi)容,更多關(guān)于TypeScript Object.keys不起作用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面方法詳解
Uniapp是一款基于Vue.js框架的跨平臺開發(fā)工具,支持在一套代碼中開發(fā)出運行于各大平臺的應用程序,這篇文章主要給大家介紹了關(guān)于Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面的相關(guān)資料,需要的朋友可以參考下2023-09-09用Javascript數(shù)組處理多個字符串的連接問題
小技巧 用Javascript數(shù)組處理多個字符串的連接問題,大家可以參考下。2009-08-08js父窗口關(guān)閉時子窗口隨之關(guān)閉完美解決方案
admin注銷的時候,或者main.html關(guān)閉的時候,如何讓打開的所有新窗口一起關(guān)閉,下面有個不錯的解決方案,大家可以參考下2014-04-04用函數(shù)模板,寫一個簡單高效的 JSON 查詢器的方法介紹
本篇文章小編將為大家介紹,用函數(shù)模板,寫一個簡單高效的 JSON 查詢器的方法介紹,需要的朋友可以參考一下2013-04-04js或者jquery判斷圖片是否加載完成實現(xiàn)代碼
需要獲得圖片的寬度和高度,有些js或者jquery代碼在還沒有加載完圖片時就執(zhí)行了,這個問題該怎么解決呢?接下來分別介紹下js與jquery提供的方法2013-03-03