繁体   English   中英

如何保证通过 object 密钥可以使用 Typescript 调用?

[英]How to guarantee that passed object key is callable using Typescript?

我正在尝试为特定用例创建一个 React 高阶组件,问题归结为以下几点:

function sample<TObj, P extends keyof TObj, F extends keyof TObj>(
  obj: TObj,
  prop: P,
  setProp: TObj[F] extends (value: TObj[P]) => void ? F : never
) {
  obj[setProp](obj[prop]);
}

I want to be able to pass an object, a string which should be a key of that object, and another key of that object but which is required to be a function.

这可以进一步简化为:

function sample2<TObj, F extends keyof TObj>(
  obj: TObj,
  setProp: TObj[F] extends () => void ? F : never
) {
  obj[setProp]();
}

在我看来,因为我使用条件类型,可以保证obj[setProp]将是 function 但我收到错误: 在此处输入图像描述

This expression is not callable.
  Type 'unknown' has no call signatures.ts(2349)

如下所示,如果使用不符合要求的键调用 function 将出错。 但同样的要求似乎不适用于 function 内部。

我知道这可以被视为一个 XY 问题,但它让我对是否有办法让这个特定问题正常工作非常感兴趣。

sample2()的实现中,类型TObj[F] extends () => void? F: never TObj[F] extends () => void? F: never是未解析的条件类型。 也就是说,它是一个条件类型,取决于要解析的当前未指定的泛型类型参数。 在这种情况下,编译器通常不知道如何处理它并将其视为本质上不透明的。 (有关此问题的一些讨论,请参见microsoft/TypeScript#23132 。)特别是它没有意识到TObj[Tobj[F] extends ()=>void? F: never] TObj[Tobj[F] extends ()=>void? F: never]最终将不得不解析为()=>void的某些子类型。

一般来说,除非必要,否则我会完全避免使用条件类型。 编译器可以更轻松地从Record<K, V>映射类型中理解和推断:

function sample2<K extends PropertyKey, T extends Record<K, () => void>>(
  obj: T,
  prop: K
) {
  obj[prop]();
}

当你调用它时,它的行为类似:

const obj2 = {
  func() { console.log("func") },
  prop: 42
};
sample2(obj2, "func"); // okay, 
//sample2(obj, "prop"); // error
//      ~~~ <-- number is not assignable to ()=>void

编辑:为了解决原始sample() ,我会使用这个定义:

function sample<
  PK extends PropertyKey,
  FK extends PropertyKey,
  T extends Record<PK, any> & Record<FK, (v: T[PK]) => void>
>(
  comp: T,
  prop: PK,
  setProp: FK
) {
  comp[setProp](comp[prop]);
}

const obj = {
  func(z: number) { console.log("called with " + z) },
  prop: 42
}

我认为,这也符合您的意愿:

sample(obj, "prop", "func"); // called with 42
sample(obj, "prop", "prop"); // error!
//     ~~~ <-- number not assignable to (v: number)=>void
sample(obj, "func", "func"); // error!
//     ~~~ <-- (v: number)=>void not assignable to number

好的,希望有帮助; 祝你好运!

链接到代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM