簡體   English   中英

TypeScript:通過讀取泛型類型的值來鍵入泛型接口的方法參數

[英]TypeScript: Typing generic interface's method parameter by reading a value from the generic type

我想定義一個能夠處理任何Data類型的通用接口。 該接口有一個dataKey屬性,它的值只是一個keyof Data 它還有一個 handler function,它的參數類型應該和使用dataKeyData讀取值的類型相同。 應該是這樣的,但這不起作用,因為Data[dataKey]無效 TypeScript:

interface Handler<Data> {
    dataKey: keyof Data,
    handler: (value: Data[dataKey]) => void
}

有沒有辦法讓它工作? 我可以使用any類型而不是Data[dataKey] ,但這並不能保證它的類型安全。

這是我想如何使用Handler接口的示例:

function handleData<Data extends object>(data: Data, handler: Handler<Data>) {
    const value = data[handler.dataKey];
    handler.handler(value);
}

interface Person {
    name: string,
    age: number,
}

const person: Person = {name: "Seppo", age: 56};
const handler: Handler<Person> = {dataKey: "name", handler: (value: string) => {
    // Here we know that the type of `value` is string,
    // as it is the type of reading `name` from the person object.
    // If I change dataKey to "age", the type of `value`
    // should be `number`, respectively
    console.log("Name:", value);
}}

handleData(person, handler);

要使Handler<Person>評估為強制執行dataKey屬性和handler回調參數類型之間相關性的類型,您非常需要它是一個聯合類型,其中一個成員用於Person的每個屬性。 它需要看起來像這樣:

interface Person {
  name: string,
  age: number,
}

type PersonHandler = Handler<Person>;
/* type PersonHandler = {
    dataKey: "name";
    handler: (value: string) => void;
} | {
    dataKey: "age";
    handler: (value: number) => void;
} */

這樣,一個Handler<Person>要么是{dataKey: "name", handler: (value: string) => void類型的值,要么是{dataKey: "age", handler: (value: number) => void

所以我們正在尋找一種方法來定義type Handler<T extends object> =...以便它生成適當的聯合類型。 (注意interface不能是聯合類型,所以我將Handler從接口更改為類型別名。)


這是一種方法:

type Handler<T> = { [K in keyof T]-?: {
  dataKey: K,
  handler: (value: T[K]) => void
} }[keyof T]

這就是microsoft/TypeScript#47109中創造的所謂的分布式 object 類型 分布式 object 類型是一種映射類型,它會立即被索引以獲取其屬性類型的聯合。 您可以驗證是否有鍵的並集type K = K1 | K2 | K3 |... | KN type K = K1 | K2 | K3 |... | KN type K = K1 | K2 | K3 |... | KN ,則分配式 object 類型{[P in K]: F<P>}[K]的計算結果為F<K1> | F<K2> | F<K3> |... | F<KN> F<K1> | F<K2> | F<K3> |... | F<KN> F<K1> | F<K2> | F<K3> |... | F<KN>

在上面的Handler<T>定義中,我們遍歷T的鍵(使用-? 映射修飾符從屬性中刪除任何可能的可選性,以抑制在輸出中浮動的不需要的undefined類型),並且對於每個鍵K ,我們計算相應的dataKey / handler對。 然后我們用同一組鍵對其進行索引,以獲得所有這些值的並集。

根據該定義, Handler<Person>是所需的類型:

type PersonHandler = Handler<Person>;
/* type PersonHandler = {
    dataKey: "name";
    handler: (value: string) => void;
} | {
    dataKey: "age";
    handler: (value: number) => void;
} */

然后作業也按需要工作:

const handler: Handler<Person> = {
  dataKey: "name", handler: value => {
    console.log("Name:", value.toUpperCase());
  }
}

請注意,您不必注釋value是一個string 編譯器能夠根據上下文推斷value必須是string ,因為Handler<Person>是一個可區分的 union ,而可區分的dataKey"name"

游樂場代碼鏈接

暫無
暫無

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

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