我有以下接口:
interface Item {
_id: string;
name: string;
amount: number;
type: 'a' | 'b' | 'c';
value: string;
}
现在,我想创建一个通用方法来更新值的一个,可以是中的任何一个。
简化,该功能看起来像:
patch(item: Item, key: any, value: any): Item {
return {
...item,
[key]: value
};
}
-
key
使用什么? -
value
使用什么?
您的原始代码将允许patch
的不幸呼叫
declare const item: Item;
patch(item, 7, false); // no error 😕
尽管7
不是Item
的有效属性密钥,而false
不是Item
或任何键的有效属性值。
让我们检查您的特定Item
接口,看看keyof
和查找类型对它有什么作用。
keyof
类型操作员采用对象类型并返回其键的类型。如果该对象没有索引签名,则该类型应该是与其已知valid键相对应的文字类型的结合:
type KeysOfItem = keyof Item
// type KeysOfItem = "_id" | "name" | "amount" | "type" | "value"
因此,您希望key
参数成为其中之一。现在,假设您有一个有效的键,例如"amount"
。然后,您可以这样查找该键的属性类型:
type ItemAmount = Item["amount"];
// type ItemAmount = number
因此,对于K
类型的key
参数是什么,您希望value
参数为Item[K]
。
因此,您的功能类型的第一张照片可能是这样的:
function patch(item: Item, key: keyof Item, value: Item[keyof Item]): Item {
return {
...item,
[key]: value
};
}
declare const item: Item;
patch(item, 7, false); // error! 7 is not a key, false is not a value
更好,但是:
patch(item, "name", 14); // no error 😕
问题在于,您使用的特定key
与您传递的value
之间没有相关性。由于 "name"
是键,而 number
是可接受的值(对于 "amount"
(,因此没有错误。
解决此问题的方法是使key
成为通用类型的K
,可以比(或"扩展"(keyof Item
更具体,然后将value
限制为类型Item[K]
:
function patch<K extends keyof Item>(item: Item, key: K, value: Item[K]): Item {
return {
...item,
[key]: value
};
}
declare const item: Item;
patch(item, 7, false); // error! 7 is not a key, false is not a value
patch(item, "name", 14); // error! 14 is not a string
patch(item, "name", "okay"); // works
patch(item, "amount", 14); // okay
,这就是我建议的。希望有帮助;祝你好运!
因为键的值类型在传递的密钥上,您应该使用通用类型参数。
- 我们可以将密钥定义为扩展键的项目(这意味着密钥可以分配给键入键的键(
- 然后使用密钥为值(item [key](索引项目类型。
结果:
function patch<Key extends keyof Item>(item: Item, key: Key, value: Item[Key]): Item {
return {
...item,
[key]: value
};
}
您可以进一步进行抽象。此功能意识到项目类型并不重要:
function patch<TItem, TKey extends keyof TItem>(item: TItem, key: Key, value: TItem[Key]): TItem {
return {
...item,
[key]: value
};
}
现在,使用您通过的任何项目,您可以修补一个值