【问题标题】:Typescript - keyof on indexed property on generic doesn't properly restrict optionsTypescript - 通用索引属性上的 keyof 没有正确限制选项
【发布时间】:2022-11-27 08:33:55
【问题描述】:

鉴于我有一个类似的界面:

export interface IHasIO {
  inputs: {
    [key: string]: string
  },
  outputs: {
    [key: string]: string
  }
}

我想创建一个函数,将其作为通用接口的实现,并确保其中一个输出键作为参数传递。

所以下面的类型定义会理想地创建一个这样的函数:

// extract the outputs property by indexing it.
export type Outputs<T extends IHasIO> = T['outputs'];

// only allow a key of one of the outputs to be the function parameter by using keyof.
export type writeToOutput<T extends IHasIO> = (param: keyof Outputs<T>) => void;

但是,如果我创建一个实现该接口的值,并将其用作通用 arg,则参数选项不受限制:

const instance: IHasIO = {
  inputs: {},
  outputs: {
    a: 'someValue',
    b: 'someOtherVal'
  }
}

// create a dummy fn
const fn: writeToOutput<typeof instance> = (param) => {
}

// this should not work, as `c` is not one of the output keys, but it does work, as it passes the typescript linting errors
fn("c");

// only these should work:
fn("a");
fn("b";

我究竟做错了什么?

【问题讨论】:

  • 通过将 instance 的类型显式注释为 IHasIO,您已经告诉编译器忘记任何比这更具体的内容。你应该放弃注解,让编译器推断它的类型;如果您关心确保它可分配给IHasIO,您可以使用 TS4.9+ 中的satisfies 运算符,如in this playground link 所示。这是否完全解决了您的问题?如果是这样,我可以写一个答案来解释;如果没有,我错过了什么? (如果您回复,请通过@jcalz 联系我)
  • @jcalz 是的,这似乎可以解决问题!

标签: typescript


【解决方案1】:

这里的问题是,通过显式地将annotatinginstance的类型设为IHasIO,您实际上已经告诉编译器它不应该尝试跟踪它的特定属性;相反,它只会知道instanceIHasIO 类型,因此inputsoutputs 属性是{[key: string]: string} 类型,这意味着keyof typeof instance["outputs"] 只是string。因此 fn() 将接受任何 string 作为输入。

如果你想要更强的类型,你应该让编译器推断instance 的类型,通过省略注释。如果你真的关心验证instance是否可以分配给IHasIO而不将其扩展到该类型,你可以使用the satisfies operator,它将在 TypeScript 4.9 中发布:

const instance = {
  inputs: {},
  outputs: {
    a: 'someValue',
    b: 'someOtherVal'
  }
} satisfies IHasIO;

但是无论有没有satisfiesinstance的类型都被推断为

/* const instance: {
    inputs: {};
    outputs: {
        a: string;
        b: string;
    };
} */

因此keyof typeof instance["outputs"]"a" | "b"。所以 fn() 现在的行为符合预期:

fn("c"); // error! 
// ~~~
// Argument of type '"c"' is not assignable to 
// parameter of type '"a" | "b"'.
fn("a"); // okay
fn("b"); // okay

Playground link to code

【讨论】:

    猜你喜欢
    • 2021-02-22
    • 1970-01-01
    • 2023-03-11
    • 2023-04-07
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多