请考虑:

const fcts = {
  fctNumber: () => 1,
  fctBoolean: () => true,
}

不出所料,简单地从fcts中 Select 的函数将返回基于值T的正确类型

const getFct = <T extends keyof typeof fcts>(name: T) => {
  return fcts[name]
}

getFct('fctNumber') // () => number
getFct('fctBoolean') // () => boolean

同样的函数,但返回调用所选函数的result,期望返回所选函数的返回类型,但TS反而推断number | boolean

const getResult = <T extends keyof typeof fcts>(name: T) => {
  const fct = fcts[name]
  return fct()
}

getResult('fctNumber') // expected number, got number | boolean
getResult('fctBoolean') // expected boolean, got number | boolean

更改最后一行以添加看似多余的断言是可行的:

return fct() as ReturnType<typeof fct>

...但为什么呢?这似乎没有增加任何新的信息

推荐答案

TypeScrip不能执行维护name类型(generic类型K)和返回类型fcts[name]之间的相关性所需的那种高阶分析.它不会将fcts"视为"零参数函数的对象,该对象返回与name的类型相关的类型.当将类型(typeof fcts)[K]作为可调用函数求值时,编译器首先将其扩展到constraint,即(typeof fcts)[keyof typeof fcts],并成为union type (() => number) | (() => boolean).一旦发生这种情况,就没有希望了,因为TypeScrip也不能对相关的联合类型做很多事情.这是microsoft/TypeScript#30581的主题.

如果你不想只是为了方便而使用type assertion,你可以try microsoft/TypeScript#47109中推荐的重构. 我们需要的是fctsmapped type,就像{ [K in keyof FctsReturn]: () => FctsReturn[K] }是一个适当定义的FctsReturn. 那么fcts[name]将是类型为() => FctsReturn[K]的单个函数,调用它将得到FctsReturn[K]. 不涉及unions .

您可以使用现有的代码来派生FctsReturn,例如将fcts重新命名为_fcts,使用ReturnType<T> utility type计算FctsReturn,然后为fcts提供必要的类型注释:

const _fcts = {
    fctNumber: () => 1,
    fctBoolean: () => true,
}
type FctsReturn = { [K in keyof typeof _fcts]: ReturnType<typeof _fcts[K]> };
const fcts: { [K in keyof FctsReturn]: () => FctsReturn[K] } = _fcts;

这看起来像是一种无所事事的迂回方式;当然,无论哪种方式,fcts的类型都是等同的.但是,由于编译器不能自动"看到"如何在fcts的元素之间分配ReturnType<T>,所以您必须自己来做.现在,这个方法起作用了:

const getResult = <K extends keyof FctsReturn>(name: K) => {
    const fct = fcts[name]
    return fct()
}

const num = getResult('fctNumber');
//    ^? const num: number

const boo = getResult('fctBoolean');
//    ^? const boo: boolean

Playground link to code

Typescript相关问答推荐

如何修复VueJS中的val类型不能赋给函数

使用前的组件渲染状态更新

Angular 17 -如何使用新的@if语法在对象中使用Deliverc值

在使用Typescribe时,是否有一种方法可以推断映射类型的键?

键集分页顺序/时间戳上的筛选器,精度不相同

TypeScript:在作为参数传递给另一个函数的记录中对函数的参数实现类型约束

迁移到Reaction路由V6时,获取模块Reaction-Router-Dom';没有导出的成员RouteComponentProps错误

如何在TypeScrip中正确键入元素和事件目标?

在包含泛型的类型脚本中引用递归类型A<;-&B

如何在编译时为不带常量的参数引发错误

诡异的文字类型--S为物语的关键

寻址对象中路径的泛型类型

从泛型类型引用推断的文本类型

如何在省略一个参数的情况下从函数类型中提取参数类型?

在TypeScript中扩展重载方法时出现问题

从以下内容之一提取属性类型

替换typescript错误;X类型的表达式可以';t用于索引类型Y;带有未定义

如何实现允许扩展泛型函数参数的类型

React HashRouter,单击导航栏时页面重新加载出现问题

如何在没有空接口的情况下实现求和类型功能?