【问题标题】:Self reference key type in a TypeScript interfaceTypeScript 接口中的自引用键类型
【发布时间】:2021-08-21 06:45:03
【问题描述】:

我想以编程方式更新具有以下接口的对象:

interface ITypes {
  num: number;
  str: string;
  bol: boolean;
};

const obj: Partial<ITypes> = {}; // I want to update this programatically

我希望能够定义 keyval 并使用这些值更新我的 obj。我想要它,以便key 只能是来自ITypes 接口的键之一,然后该值需要是从接口中选择的key 指定的类型。我可以使用以下代码很好地做到这一点:

const key: keyof ITypes = "num"; // key must be a key from the ITypes interface
const val: ITypes[typeof key] = 1; // val must be the type specified at `"num"` - `number`
obj[key] = val;

上面的代码工作正常,但是,现在,我不想将keyval 作为单独的变量,而是想在一个对象中定义它们,所以我需要定义一个接口。但是,val 的类型定义有问题。这是我迄今为止尝试过的:

interface IUpdatePayload {
  key: keyof ITypes;
  val: ITypes[typeof this.key]; // Type 'any' cannot be used as an index type.
};
const updatePayload: IUpdatePayload = {key: "num", val: "1"}; // should complain that `val` is not a number
obj[updatePayload.key] = updatePayload.val; 

我尝试使用typeof this.key(我在this answer 中看到建议)自引用接口的key 类型,但是Type 'any' cannot be used as an index type. 出错,我猜这是因为密钥没有实际上被定义为具有像第一个使用变量的工作示例中的值。我的问题是,有没有办法让这个工作,让val 采用key 定义的类型,如ITypes 接口中指定的那样?

【问题讨论】:

    标签: typescript interface


    【解决方案1】:

    您可以使用映射类型从任何类型生成键/值对的联合:

    type KVPairs<T, K extends keyof T = keyof T> = 
        K extends keyof T
            ? { key: K, val: T[K] } 
            : never;
    

    然后您可以创建自定义联合:

    // {key: "num", val: number} | {key: "str", val: string} | {key: "bol", val: boolean}
    type IUpdatePayload = KVPairs<ITypes>;
    

    此类型还允许您根据需要选择键的子集:

    // {key: "num", val: number} | {key: "str", val: string}
    type NumOrStr = KVPairs<ITypes, "num"|"str">
    

    但是,将对象字面量分配给联合类型时,错误并不总是很清楚:

    const obj: IUpdatePayload = { key: "num", val: "1" };
    /*
    Type '{ key: "num"; val: string; }' is not assignable to type 'IUpdatePayload'.
      Type '{ key: "num"; val: string; }' is not assignable to type '{ key: "bol"; val: boolean; }'.
        Types of property 'key' are incompatible.
          Type '"num"' is not assignable to type '"bol"'.
    */
    

    playground

    【讨论】:

      【解决方案2】:

      您可以为此使用通用接口:

      interface IUpdatePayload<K extends keyof ITypes> {
        key: K;
        val: ITypes[K];
      }
      
      const updatePayload: IUpdatePayload<"num"> = {
        key: "num",
        val: "1"
      //~~~ Type 'string' is not assignable to type 'number'.
      };
      obj[updatePayload.key] = updatePayload.val; 
      

      这样做的缺点是您必须指定通用参数"num"(您要更新的属性)。为避免这种情况,您可以使用一个辅助函数来为您推断此参数:

      const updateObj = <K extends keyof ITypes>(updatePayload: IUpdatePayload<K>): void => {
        obj[updatePayload.key] = updatePayload.val; 
      }
      updateObj({key: "num", val: "1"}) // error
      updateObj({key: "num", val: 1}) // ok
      

      Playground link

      【讨论】:

        猜你喜欢
        • 2018-09-08
        • 1970-01-01
        • 2018-01-16
        • 1970-01-01
        • 2021-06-28
        • 1970-01-01
        • 2020-02-02
        • 2021-01-24
        相关资源
        最近更新 更多