Typescript 无法为函数对象推断正确的参数类型 [英] Typescript cannot infer correct argument types for an object of functions

查看:39
本文介绍了Typescript 无法为函数对象推断正确的参数类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个更新函数,它接受一个对象 T 和一个updater"对象,它采用与 T 相同的键(或子集)并提供了更新原始对象对应值的函数.

type UpdaterObj= {[部分T的密钥中的K]:(t:T[K])=>T[K]};声明函数更新(obj: T, updaterObj: UpdaterObj): T

示例用法如下:

const foo = {x: 1,y:1"};更新(富,{x: (xx: 数字) =>xx + xx,y: (yy: 字符串) =>YY + YY,});//结果是 { x: 2, y: "11" }

这行得通,但我真的很想删除那些注释,只写 x: (xx) =>;xx + xxy: (yy) =>yy + yy,但是对于 xxyy,我得到 Parameter 隐式具有类型 'any' 错误.

这很奇怪,因为如果我将鼠标悬停在 x: 上,它会正确推断出相应的值应该是什么,但如果我注释 xxyy 作为错误的类型,我很确定我以前见过打字稿做出类似的推论.

在这种情况下,注释非常简单,但如果我深入了解对象或使用更复杂的类型,可能会很烦人,因此在这里推断会非常有用.

对我的定义或用法有什么简单的调整可以让打字稿为我推断出来吗?

解决方案

我不确定 GitHub 关于函数参数的类型推断如何与泛型类型参数的推断进行交互,所以我不知道是否对函数签名进行了简单的调整,使其在不影响调用站点的情况下工作.

查看 TypeScript 规范,我看到对于 TypeScript 函数表达式(例如 xx => xx + xx),

<块引用>

当一个没有类型参数也没有参数类型注解的函数表达式被一个类型T进行上下文类型化时,可以从S中提取一个上下文签名Scode>T,函数表达式被处理,就好像它已显式指定参数类型注释,因为它们存在于 S 中.

[截图]

从函数类型T中提取上下文签名S如下:

  • 如果 T 是一个只有一个调用签名的函数类型,并且如果调用签名是非通用的S是那个签名.

  • 如果 T 是联合类型... [snip]

  • 否则,无法从 T 中提取上下文签名.

所以它看起来像因为 (xx => xx + xx) 在您的原始调用中具有 (t: T['x']) 形式的函数类型=>T['x'] 包含类型参数 T,它不会像具有显式类型参数一样处理函数.大概这意味着编译器会在消除泛型参数之前尝试提取上下文签名.

<小时>

如果可以更改调用站点,那么我可以提出的最简单的建议是将两个参数的 update() 函数分解为 咖喱 函数如下:

声明函数 updateCurried(obj: T): (updaterObj: UpdaterObj) =>吨

如下使用(注意函数调用的顺序):

updateCurried(foo)({x: xx =>xx + xx,y:yy =>YY + YY,});//好的

这实质上迫使编译器在评估 updaterObj 参数时评估和消除泛型类型参数,此时它可以进行上下文函数参数推断,如上文所述摘自规范.

希望对你有帮助.祝你好运!

I've written an update function that takes an object, T, and an "updater" object, which takes the same keys (or a subset) as T and provides a function for updating the corresponding value of the original object.

type UpdaterObj<T> = {
    [K in keyof Partial<T>]: (t: T[K]) => T[K]
};
declare function update<T>(obj: T, updaterObj: UpdaterObj<T>): T

Sample usage is like this:

const foo = {
    x: 1,
    y: "1"
};

update(foo, {
    x: (xx: number) => xx + xx,
    y: (yy: string) => yy + yy,
}); // Result is { x: 2, y: "11" }

This works, but I'd really like to remove those annotations and just write x: (xx) => xx + xx and y: (yy) => yy + yy, however I get the Parameter implicitly has type 'any' error, for both xx and yy.

It's odd because if I mouse over x:, it correctly infers what the corresponding value should but, and it complains if I annotate xx or yy as the wrong type, and I'm pretty sure I've seen typescript make similar inferences before.

The annotations are pretty simple in this case, but can get annoying if I'm deep within objects or with more complex types, so inference would be really helpful here.

Is there any simple tweak to my definitions or my usage that can get typescript to infer this for me?

解决方案

I'm not sure if there's an existing issue in GitHub about how type inference of function parameters interacts with inference for generic type parameters, so I don't know if there's a simple tweak of the function signature which makes it work without affecting the call site.

Looking at the TypeScript spec, I see that for TypeScript function expressions (like xx => xx + xx),

When a function expression with no type parameters and no parameter type annotations is contextually typed by a type T and a contextual signature S can be extracted from T, the function expression is processed as if it had explicitly specified parameter type annotations as they exist in S.

[snip]

A contextual signature S is extracted from a function type T as follows:

  • If T is a function type with exactly one call signature, and if that call signature is non-generic, S is that signature.

  • If T is a union type... [snip]

  • Otherwise, no contextual signature can be extracted from T.

So it kind of looks like since (xx => xx + xx) in your original call has a function type of the form (t: T['x']) => T['x'] that includes the type parameter T, it doesn't process the function as if it had explicit type parameters. Presumably that means the compiler tries to extract a contextual signature before it eliminates the generic parameter.


If it's okay to change the call site, then the simplest suggestion I can make which demonstrably works is to break the two-argument update() function into a curried function like so:

declare function updateCurried<T>(obj: T): (updaterObj: UpdaterObj<T>) => T

which would be used as follows (note the sequence of function calls):

updateCurried(foo)({
  x: xx => xx + xx,
  y: yy => yy + yy,
}); // okay

This essentially forces the compiler to evaluate and eliminate the generic type parameter by the time it gets to evaluating the updaterObj prarameter, at which point it can do the contextual function parameter inference as described in the above excerpt from the spec.

Hope that's helpful to you. Good luck!

这篇关于Typescript 无法为函数对象推断正确的参数类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆