function run<U>(func1: (arg: U) => void, func2: (arg: { a: string }) => U): void {}

run((params) => {}, () => 23); // The params here can be correctly inferred as number

run((params) => {}, (arg) => 23); // Why is the params here inferred to be unknown?

我希望PARAMS在任何情况下都能自动推断正确的类型

推荐答案

TypeScrip有一个普遍的限制,它不能总是同时推断generic个类型参数和某个表达式的contextual type个,而这些至少在句法上是有可能循环地相互依赖的.TypeScrip的推理算法不使用microsoft/TypeScript#30134中讨论的所谓的"Full unification",因此总是会有一些情况下,它无法推断出您所期望的东西.在这些情况下,您始终可以只指定annotate your callback parameters或手动指定泛型类型参数.


在您的特定示例中,给定调用签名

function run<U>(func1: (arg: U) => void, func2: (arg: { a: string }) => U): void {}

呼唤

run((params) => {}, () => 23);

行为符合预期,因为可以从() => 23的返回类型推断出U,而且() => 23不是context-sensitive.已知它是() => number类型的,而不必判断在其中发现它的context.回调(params) => {}是上下文敏感的,其中params是类型U.由于已知Unumber,则推断params是类型number.

On the other hand, 呼唤

run((params) => {}, (arg) => 23);

并不像预期的那样工作.同样,U需要从(arg) => 23的返回类型推断出来. 但是(arg) => 23is是上下文敏感的. 它的类型取决于arg的类型,而arg的类型取决于表达式所在的上下文.TypeScript决定在这里执行defer. 从这里开始一切都被分解了;唯一可以推断出U的地方是参数类型(params) => {},但这也是上下文敏感的.所以对于U没有什么可以推断的.推理失败,U返回到unknownparams属于unknown类型.


当然,现在(arg) => 23的返回类型不是actually依赖于arg的类型,因为23无论如何都是number.因此,应该可以想象修复它的编译器改进.开放特征请求microsoft/TypeScript#47599是关于循环依赖仅为apparent的情况.

事实上,在类似案件中取得了一些进展. TypeScript 4.7引入了improved function inference in objects and methods,在microsoft/TypeScript#48538中实现:

当在上下文中键入泛型函数参数列表中的箭头函数、函数表达式和对象文字方法的参数时,我们从参数列表and from context sensitive function arguments in preceding positions in the argument list.中的任何位置的context insensitive个函数参数推断类型

这意味着,如果您重新排列函数参数,以便可以从first参数而不是second参数推断出U,那么您应该能够获得所需的推论.这样,推理就可以从左到右而不是从右到左:

function run2<U>(
    func2: (arg: { a: string }) => U,
    func1: (arg: U) => void
): void { }

run2(() => 23, (params) => { },);
//              ^?(parameter) params: number
run2((arg) => 23, (params) => { },);
//                ^?(parameter) params: number

这是可行的,因为即使(arg)=>23也是(params)=>{}之前的上下文敏感函数参数.这样的重构并不总是可接受的(例如,您不能更改现有的库),有时不存在可以工作的重构(参见顶部段落),因此它在很大程度上取决于您的用例如何进行.

Playground link to code

Typescript相关问答推荐

即使子网是公共的,AWS CDK EventBridge ECS任务也无法分配公共IP地址

将对象属性转换为InstanceType或原样的强类型方法

如何使用泛型自动推断TS中的类型

对扩展某种类型的属性子集进行操作的函数

编剧错误:正在等待Expect(Locator).toBeVisible()

从子类构造函数推断父类泛型类型

如何在Type脚本中动态扩展函数的参数类型?

ANGLE独立组件布线错误:没有ActivatedRouting提供程序

如何避免推断空数组

ANGLING v17 CLI默认设置为独立:延迟加载是否仍需要@NgModule进行路由?

如何将对象数组中的键映射到另一种对象类型的键?

是否可以强制静态方法将同一类型的对象返回其自己的类?

有没有办法在Zod中使用跨字段验证来判断其他字段,然后呈现条件

TS2739将接口类型变量赋值给具体变量

如何在不违反挂钩规则的情况下动态更改显示内容(带有状态)?

为什么在这种情况下,打字脚本泛型无法正确自动完成?

使用来自API调用的JSON数据的Angular

打字脚本错误:`不扩展[Type]`,而不是直接使用`[Type]`

类型脚本中函数重载中的类型推断问题

在tabel管理区域react js上设置特定顺序