我目前正在努力打字,以便输入我的配置,整个应用程序都在使用它.配置的类型是配置,它使用键值保存在对象中.通过使用助手函数,我定义了函数参数的类型.

// Base object types
export type Config<D> = {
  fn: ({ option }: { option: D }) => any;
  option: D;
};

export type ConfigRecord<T extends Record<keyof T, any>> = {
  [K in keyof T]: Config<T[K]> & { type: K };
};

// Helper function to type function argument
export const typeConfigRecord = <T extends Record<keyof T, any>>(
  map: ConfigRecord<T>
) => map;

// Actual implementation of type
const configRecord = typeConfigRecord({
  test: {
    type: "test",
    fn: ({ option }) => {
      return option;
    },
    option: "test",
  },
  test2: {
    type: "test2",
    fn: ({ option }) => {
      return option;
    },
    option: 2,
  },
});

现在,使用以下方法调用配置可以正常工作,并且可以正确推断参数类型:

configRecord.test.fn({ option: "test" });

但是,当我按如下方式动态执行函数而不是UNION时,函数参数变成所有可能值的交集,导致它出错:

const dynamicExecute = (type: keyof typeof configRecord) => {
  const config = configRecord[type];

  return config.fn({ option: config.option }); // Error, type is never
}

我已经研究并看到了术语关联联合,我试图找出哪里出了问题,但我在想,因为它是一个嵌套属性,所以它让我绊倒了.任何建议都将不胜感激.

推荐答案

是的,正如microsoft/TypeScript#30581中所讨论的,这本质上是一个相关的union问题.编译器无法分析单个代码块

const config = configRecord[type];
return config.fn({ option: config.option }); 

其中config是联合类型,请注意{option: config.option}config.fn以正确的方式相关联,从而使它们匹配.相反,它只是将{option: config.option}config.fn都视为独立的联合类型,因此将参数联合传递给函数联合是不安全的.我的意思是,如果有相同联合类型的config1config2,就不能安全地调用config1.fn({option: config2.option}),而且因为编译器只跟踪这里的types,而不跟踪configconfig1/config2identities,所以它不能区分它们之间的区别.


在这种情况下,推荐的方法是从unions 切换到generics.microsoft/TypeScript#47109中详细介绍了一种相当复杂的技术,但第一步是将联合类型替换为联合的泛型类型constrained.

如果你在这里这样做,事情就会好起来:

const dynamicExecute = <K extends keyof typeof configRecord>(type: K) => {
  const config = configRecord[type];
  return config.fn({ option: config.option }); // okay
}

这是因为您拥有的类型基本上已经采用了MS/TS#47109中描述的正确形式(基本键值接口上的mapped type),而您唯一缺少的就是使用泛型.

Playground link to code

Typescript相关问答推荐

如何从具有给定键列表的对象类型的联合中构造类型

参数类型undefined不能分配给参数类型字符串|未定义

如何在TypeScript对象迭代中根据键推断值类型?

JS Redux Devtools扩展不工作

在键和值为ARGS的泛型函数中未正确推断类型

在NextJS中获得Spotify Oauth,但不工作'

异步动态生成Playwright测试,显示未找到测试

根据另一个属性的值来推断属性的类型

对数组或REST参数中的多个函数的S参数进行类型判断

Vue SFC中的多个脚本块共享导入的符号

参数属性类型定义的REACT-RUTER-DOM中的加载器属性错误

嵌套对象的类型

已解决:如何使用值类型限制泛型键?

使用2个派生类作为WeakMap的键

使用KeyOf和Never的循环引用

有没有办法防止类型交集绕过联合类型限制?

如何在TypeScript中描述和实现包含特定属性的函数?

为什么类方法参数可以比接口参数窄

两个接口的TypeScript并集,其中一个是可选的

Typescript 子类继承的方法允许参数中未定义的键