在TypeScript中迭代對象鍵Object.keys不起作用的原因和解決方案
原文鏈接:https://www.totaltypescript.com/iterate-over-object-keys-in-typescript
在TypeScript中迭代對象鍵object keys可能是一場噩夢。以下是我所知道的所有解決方案。
快速解釋 - 省流
使用 Object.keys 迭代不起作用,是因?yàn)?Object.keys 返回一個(gè)字符串?dāng)?shù)組,而不是所有鍵的并集。這是設(shè)計(jì)出來的,不會改變。
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]);
}
});
更長的解釋
這里有一個(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[]
這意味著你不能使用鍵來訪問對象上的值:
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對象類型是開放式的。
在很多情況下,TS不能保證 Object.keys 返回的鍵實(shí)際上在對象上,因此將它們擴(kuò)展為字符串是唯一合理的解決方案。查看此問題以了解更多詳細(xì)信息。
在For循環(huán)中
如果你嘗試做一個(gè)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'.
}
}
但在許多情況下,你實(shí)際上能夠確認(rèn)對象是什么樣的。所以,你是怎么做的?
解決方案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]);
});
我們也可以在索引到對象的時(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];
對于這種情況, as 是一個(gè)相當(dāng)強(qiáng)大的工具-正如你所看到的,它讓我們對TypeScript撒謊。
解決方案2:類型謂詞 Type Predicates
讓我們來看看一些更聰明,可能更安全的解決方案。類型謂詞怎么樣?通過使用 isKey helper,我們可以在索引到對象之前檢查鍵是否確實(shí)在對象上。我們通過在 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)部的方法會使其更具吸引力,但可能導(dǎo)致人們在使用時(shí)不加思考。
結(jié)論
我的首選解決方案?通常情況下,類型轉(zhuǎn)換非常完美地完成了工作。它簡單易懂,并且通常足夠安全。
但是如果你喜歡類型謂詞或通用解決方案的外觀,請盡管使用。isKey函數(shù)看起來很有用,我會在下一個(gè)項(xiàng)目中借用它。
以上就是在TypeScript中迭代對象鍵Object.keys不起作用的原因和解決方案的詳細(xì)內(nèi)容,更多關(guān)于TypeScript Object.keys不起作用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
HTML5+setCutomValidity()函數(shù)驗(yàn)證表單實(shí)例分享
本文給大家分享的是在HTML5中結(jié)合setCutomValidity()函數(shù)實(shí)現(xiàn)驗(yàn)證表單的實(shí)例,非常的時(shí)間實(shí)用,這里推薦給大家,有需要的小伙伴可以參考下。2015-04-04
JS實(shí)現(xiàn)給json數(shù)組動態(tài)賦值的方法示例
這篇文章主要介紹了JS實(shí)現(xiàn)給json數(shù)組動態(tài)賦值的方法,結(jié)合實(shí)例形式分析了javascript針對json數(shù)組的遍歷、賦值等常用操作技巧,需要的朋友可以參考下2017-07-07
鼠標(biāo)移動到圖片名上,顯示圖片的簡單實(shí)例
鼠標(biāo)移動到名(wait.gif)上,顯示圖片,鼠標(biāo)移開則不顯示圖片2013-07-07
Base64(二進(jìn)制)圖片編碼解析及在各種瀏覽器的兼容性處理
這篇文章主要介紹了Base64(二進(jìn)制)圖片編碼解析及在各種瀏覽器的兼容性處理,需要的朋友可以參考下2017-02-02
echartjs實(shí)現(xiàn)cross十星輔助線實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了echartjs實(shí)現(xiàn)cross十星輔助線實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
為網(wǎng)站代碼塊pre標(biāo)簽增加一個(gè)復(fù)制代碼按鈕代碼
寫文章的時(shí)候,作為一名專業(yè)的碼農(nóng),經(jīng)常會在文章中粘貼一些代碼。有的時(shí)候代碼塊比較長,在后期使用中需要復(fù)制這段代碼就比較麻煩2021-11-11

