高阶函数参数类型的错误类型推断
Posted
技术标签:
【中文标题】高阶函数参数类型的错误类型推断【英文标题】:Wrong type inference of higher-order function argument type 【发布时间】:2020-01-24 18:41:28 【问题描述】:假设我想编写一个函数,它接受某种类型 T 的对象和另一个值,该类型 P 应该以某种方式受 T 限制,例如 P 应该是 T 的键数组。
我可以很容易地写出来:
function bar<T, P extends keyof T>(obj: T, p: P[])
// use p to index obj somehow
return obj;
bar( a: 1, b: 'foo' , ['a']); // Ok
bar( a: 1, b: 'foo' , ['a', 'b']); // Ok
bar( a: 1, b: 'foo' , ['a', 'b', 'c']); // Error: 'c' is not valid key
想象一下,然后我想将该函数用作高阶方法的参数,它应该与第二个参数 arg
一起接受它,然后用 this
和 arg
调用它:
class Indexed
constructor(public a: number = 1)
public app<P>(f: (obj: this, arg: P) => this, arg: P)
return f(this, arg);
const a = new Indexed().app(bar, ['a']); // Error, `Type 'string' is not assignable to type '"a" | "app"'.`
const a = new Indexed().app(bar, ['wtf']); // The same
如果我直接使用bar
,一切都会按预期进行:
bar(new Indexed(), ['a']); // Ok
bar(new Indexed(), ['wtf']); // Err, as expected
Playground
问题是:如何编写app
使其接受/拒绝参数的方式与bar
相同?
请注意,一般我不知道 bar
先验的限制,所以我不能用与 bar
相同的界限来限制 P
。
【问题讨论】:
【参考方案1】:我认为这只是 TypeScript 将 ["foo","bar"]
扩展为 string[]
的一种情况,因为它没有意识到您需要该类型来保持字符串文字元组 ["foo", "bar"]
(或至少是字符串文字数组 @ 987654326@)。在您的 bar()
函数中,P
被限制为 keyof
任何提示编译器不要将字符串文字扩展为字符串,但在 Indexed.app()
中不存在 P
的此类提示。
您需要想出一种方法来修改Indexed.app()
签名,以暗示P
应该在可能的情况下以狭窄的方式推断而无需实际限制它(因为您不知道P
将如您所说),或者您需要想出一种方法来提示/指定P
在您调用 Indexed.app()
时应该是狭窄的。
修改app()
的签名来做到这一点目前需要一些奇怪的技巧,除非这个changes,否则它看起来像这样:
type Narrowable =
| string
| number
| boolean
| symbol
| object
| undefined
| void
| null
| ;
class Indexed
constructor(public a: number = 1)
public app<
N extends Narrowable,
P extends N | [] | [k: string]: N | P | []
>(f: (obj: this, arg: P) => this, arg: P)
return f(this, arg);
const a = new Indexed().app(bar, ["a"]); // okay
const b = new Indexed().app(bar, ["wtf"]); // error "wtf" not assignable to "a"|"app"
如果调用者记得这样做,调用站点的提示就不会那么难看:
class Indexed
constructor(public a: number = 1)
public app<P>(f: (obj: this, arg: P) => this, arg: P)
return f(this, arg);
const a = new Indexed().app(bar, ["a" as "a"]); // okay
const b = new Indexed().app(bar, ["wtf" as "wtf"]); // error "wtf" not assignable to "a"|"app"
或者你也可以忘记提示,自己手动指定类型参数:
const c = new Indexed().app<["a"]>(bar, ["a"]); // okay
const d = new Indexed().app<["wtf"]>(bar, ["wtf"]); // error "wtf" not assignable to "a"|"app"
好的,希望其中一个对您有所帮助。祝你好运!
Link to code
【讨论】:
以上是关于高阶函数参数类型的错误类型推断的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin函数 ⑤ ( 匿名函数变量类型推断 | 匿名函数参数类型自动推断 | 匿名函数又称为 Lambda 表达式 )