簡體   English   中英

在 TypeScript 中,如何獲取值屬於給定類型的 object 類型的鍵?

[英]In TypeScript, how to get the keys of an object type whose values are of a given type?

我一直在嘗試創建一個類型,該類型由T類型的鍵組成,其值為字符串。 在偽代碼中,它是keyof T where T[P] is a string

我能想到的唯一方法是分兩步:

// a mapped type that filters out properties that aren't strings via a conditional type
type StringValueKeys<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };

// all keys of the above type
type Key<T> = keyof StringValueKeys<T>;

然而,TS 編譯器說Key<T>簡單地等於keyof T ,即使我已經通過將它們設置為never使用條件類型來過濾掉其值不是字符串的鍵。

所以它仍然允許這樣做,例如:

interface Thing {
    id: string;
    price: number;
    other: { stuff: boolean };
}

const key: Key<Thing> = 'other';

key的唯一允許值真的應該是"id" ,而不是"id" | "price" | "other" "id" | "price" | "other" "id" | "price" | "other" ,因為其他兩個鍵的值不是字符串。

鏈接到 TypeScript 操場中的代碼示例

這可以通過條件類型索引訪問類型來完成,如下所示:

type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];

然后你拉出屬性匹配string的鍵,如下所示:

const key: KeysMatching<Thing, string> = 'other'; // ERROR!
// '"other"' is not assignable to type '"id"'

詳細:

KeysMatching<Thing, string> ➡

{[K in keyof Thing]-?: Thing[K] extends string ? K : never}[keyof Thing] ➡

{ 
  id: string extends string ? 'id' : never; 
  price: number extends string ? 'number' : never;
  other: { stuff: boolean } extends string ? 'other' : never;
}['id'|'price'|'other'] ➡

{ id: 'id', price: never, other: never }['id' | 'price' | 'other'] ➡

'id' | never | never ➡

'id'

請注意,您在做什么:

type SetNonStringToNever<T> = { [P in keyof T]: T[P] extends string ? T[P] : never };

實際上只是將非字符串屬性轉換為never屬性值。 它沒有接觸按鍵。 你的Thing會變成{id: string, price: never, other: never} 並且那個的鍵和Thing的鍵是一樣的。 KeysMatching的主要區別在於您應該選擇鍵,而不是值(因此P而不是T[P] )。

Playground 鏈接到代碼

作為補充答案:

從 4.1 版開始,您可以利用密鑰重映射作為替代解決方案(請注意,核心邏輯與 jcalz 的答案沒有區別)。 簡單地過濾掉在用於索引源類型時不會產生可分配給目標類型的類型的鍵,並使用keyof提取剩余鍵的並keyof

type KeysWithValsOfType<T,V> = keyof { [ P in keyof T as T[P] extends V ? P : never ] : P };

interface Thing {
    id: string;
    price: number;
    test: number;
    other: { stuff: boolean };
}

type keys1 = KeysWithValsOfType<Thing, string>; //id -> ok
type keys2 = KeysWithValsOfType<Thing, number>; //price|test -> ok

操場


正如Michal Minich正確提到的:

兩者都可以提取字符串鍵的並集。 然而,當它們應該在更復雜的情況下使用時——比如 T extends Keys...<T, X> 那么 TS 無法很好地“理解”你的解決方案。

因為上面的類型沒有用keyof T索引,而是使用映射類型的keyof ,編譯器不能推斷T可以被輸出聯合索引。 為了確保編譯器知道這一點,可以將后者與keyof T相交:

type KeysWithValsOfType<T,V> = keyof { [ P in keyof T as T[P] extends V ? P : never ] : P } & keyof T;

function getNumValueC<T, K extends KeysWithValsOfType<T, number>>(thing: T, key: K) {
    return thing[key]; //OK
}

更新的游樂場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM