如何在现有但未知的键/值对上设置接口



我有以下接口:

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
    };
}

现在,使用您通过的任何项目,您可以修补一个值

最新更新