说我想编写一个函数,该函数接受某个类型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
进行调用:< / p>
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
问题是:如何编写app
,使其以与bar
相同的方式接受/拒绝参数?
请注意,通常我不知道对bar
的先验限制,所以我不能对P
进行与bar
相同的限制。
答案 0 :(得分:3)
我认为这只是TypeScript将["foo","bar"]
扩展为string[]
的一种情况,因为它没有意识到您需要该类型来保留字符串文字["foo", "bar"]
的元组(或者至少是字符串文字Array<"foo"|"bar">
的数组)。在您的bar()
函数中,P
被约束为keyof
,这表明编译器没有将字符串文字扩展为字符串,但是{{1}中的P
没有这样的提示}。
您要么需要提出一种修改Indexed.app()
签名的方法,以提示Indexed.app()
应该尽可能以狭窄的方式进行推断,而实际上并不对其进行限制(因为您不知道该做什么) P
就像您所说的那样),或者您需要想出一种方法来提示/指定在您呼叫时,P
应该变窄 P
。
修改Indexed.app()
的签名以执行此操作目前需要一些怪异的技巧,并且直到且除非此changes,它看起来像这样:
app()
如果呼叫者记得这样做,则在呼叫站点上的提示不太难看:
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"
或者您可以忘记提示,而自己手动指定type参数:
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"
好的,希望其中之一能有所帮助。祝你好运!