![](/img/trans.png)
[英]Generic method's typing with generic type as a parameter does not work properly
[英]TypeScript: Typing generic interface's method parameter by reading a value from the generic type
我想定義一個能夠處理任何Data
類型的通用接口。 該接口有一個dataKey
屬性,它的值只是一個keyof Data
。 它還有一個 handler function,它的參數類型應該和使用dataKey
從Data
讀取值的類型相同。 應該是這樣的,但這不起作用,因為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.