在日常的TypeScript開發中,經常需要遍歷對象的鍵來執行各種操作。然而,使用Object.keys時可能會遇到一些類型相關的困擾,因為它返回的是一個字符串數組,而不是期望的鍵的聯合類型。這可能導致在代碼中引入一些不安全的類型轉換。在本文中,我們將深入研究這個問題,并提供幾種解決方案,以便在遍歷對象鍵時更安全、更靈活地操作。
使用Object.keys進行遍歷并不能按照預期工作。這是因為Object.keys返回一個字符串數組,而不是包含所有鍵的聯合類型。這是設計上的考慮,不會改變。
function printUser(user: User) { Object.keys(user).forEach((key) => { // 不起作用! console.log(user[key]); // 報錯:屬性“key”在類型“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]);});
通過自定義類型斷言,可以在行內縮小類型:
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]); // key現在被縮小為 "name" | "age" }});
問題在于使用Object.keys似乎無法按照期望的方式工作。這是因為它不會返回你需要的類型。
const user = { name: "Daniel", age: 26,};const keys = Object.keys(user);// keys的類型是 string[]
這意味著你不能使用鍵來訪問對象上的值:
const nameKey = keys[0];user[nameKey];// 報錯:屬性“nameKey”在類型“{ name: string; age: number; }”上不存在。
TypeScript之所以返回字符串數組,是因為它的對象類型是開放的。在許多情況下,TS無法保證由Object.keys返回的鍵實際上存在于對象上 - 因此將它們擴展為字符串是唯一合理的解決方案。
如果嘗試使用for...in循環,同樣會失敗,原因是鍵被推斷為字符串,就像Object.keys一樣。
function printUser(user: User) { for (const key in user) { console.log(user[key]); // 報錯:屬性“key”在類型“User”上不存在。 }}
但在許多情況下,你可能確信自己完全了解對象的形狀。
那么,怎么辦呢?
第一種選擇是使用keyof typeof將鍵轉換為更具體的類型。
const user = { name: "Daniel", age: 26,};const keys = Object.keys(user) as Array<keyof typeof user>;keys.forEach((key) => { // 不再報錯! console.log(user[key]);});
在索引對象時也可以進行轉換。
const keys = Object.keys(user);keys.forEach((key) => { console.log(user[key as keyof typeof user]);});
然而,as在任何形式中通常是不安全的,這也不例外。
const user = { name: "Daniel", age: 26,};const nonExistentKey = "id" as keyof typeof user;// 沒有錯誤!const value = user[nonExistentKey];// as是一個強大的工具,它允許我們在類型上欺騙TypeScript
通過使用isKey助手,可以在索引之前檢查鍵是否實際存在于對象中。
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]); // key現在被縮小為 "name" | "age" }});
再來看一個略微奇怪的解決方案。在泛型函數內部,使用in運算符將類型縮小到鍵。
function printEachKey<T extends object>(obj: T) { for (const key in obj) { console.log(obj[key]); // key的類型被縮小為Extract<keyof T, string> }}// 每個鍵都被打印出來!printEachKey({ name: "Daniel", age: 26,});
另一種解決方案是將Object.keys包裝在一個返回轉換類型的函數中。
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);// keys的類型是("name" | "age")[]
這可能是最容易被濫用的解決方案 - 將轉換隱藏在函數中使其更有吸引力,可能導致人們在不考慮的情況下使用它。
本文介紹了一些解決方案,從簡單的類型轉換到更智能的類型謂詞,幫助我們更安全、更可靠地進行對象鍵的遍歷。選擇哪種方法取決于項目的需求和個人偏好,但總體而言,通過了解這些技術,我們可以更好地利用TypeScript的類型系統,提高代碼的可維護性和安全性。
本文鏈接:http://www.www897cc.com/showinfo-26-34902-0.htmlTypeScript中遍歷對象鍵的方法
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 分享一個 Python 處理音頻的庫