你可以做一些你想做的事情,但有一些注意事项:
你必须把你的函数分解成几个:
泛型类型参数T 需要由调用者显式指定,但函数参数也需要是泛型的(例如A extends keyof T)。您不能拥有带有 <T, A, B> 之类的参数的函数,其中 T 需要手动指定,但 A 和 B 需要从参数中推断出来。你基本上从类型参数推断中得到all or nothing。解决此问题的一种方法是使用curried function,其中myFunction<T>() 返回另一个以<A, B> 作为类型参数的函数。也就是说,改变这个:
// F<> is just a placeholder
declare myFunction<T, A, B>(args: [A, B], funcs: F<T,A,B>): void;
// specify everything, annoying
myFunction<MyInterface, 'foo', 'bar'>(['foo', 'bar'], ([foo, bar])=>null);
// specify nothing, doesn't work
myFunction(['foo', 'bar'], ([foo, bar])=>null); // error
到
myFunction<T>(): <A, B>(args: [A, B], funcs: F<T,A,B>) => void
myFunction<MyInterface>()(['foo','bar'], ([foo, bar])=>null); // works
您可以看到它如何允许您在 myFunction<MyInterface>() 中指定 MyInterface,并允许编译器在后续调用中推断 A 和 B。
TypeScript 缺少variadic kinds,因此实际上不可能为许多作用于“任意长度的元组”的函数提供单一签名。您最终需要通过提供overloaded 函数签名来解决此问题,该函数签名作用于您关心的特定长度的元组。 T 键元组到 T 对应值元组的“简单”映射,您希望将其表示为类似
declare myFunction<T,K extends keyof T>(
keys: [...K], funcs: ([...T[K]]) => void): void;
最终扩展到
// one-tuple
declare myFunction<T, A extends keyof T>(
keys: [A], funcs: ([T[A]) => void): void;
// two-tuple
declare myFunction<T, A extends keyof T, B extends keyof T>(
keys: [A,B], funcs: ([T[A],T[B]) => void): void;
// three-tuple
declare myFunction<T, A extends keyof T, B extends keyof T, C extends keyof T>(
keys: [A,B,C], funcs: ([T[A],T[B],T[C]) => void): void;
// et cetera
只要你能站立。
把它放在一起:
所以,让我们像这样进行 curry 和重载:
declare function myFunction<T>(): {
<A extends keyof T, B extends keyof T, C extends keyof T, D extends keyof T>(
a: [A, B, C, D], f: (a: [T[A], T[B], T[C], T[D]]) => void
): void;
<A extends keyof T, B extends keyof T, C extends keyof T>(
a: [A, B, C], f: (a: [T[A], T[B], T[C]]) => void
): void;
<A extends keyof T, B extends keyof T>(
a: [A, B], f: (a: [T[A], T[B]]) => void
): void;
<A extends keyof T>(
a: [A], f: (a: [T[A]]) => void
): void;
}
它可以处理长度为 4 的元组。如果您愿意,请随意添加更多重载。让我们看看它是否有效:
myFunction<MyInterface>()(
['foo'],
([foo]) => null // foo is a string
);
myFunction<MyInterface>()(
['abc'], // error, '"abc"' is not '"foo" | "bar" | "baz"'.
([abc]) => null // error, abc is implicitly any
);
myFunction<MyInterface>()(
['bar', 'baz'],
([bar, baz]) => null // bar is number, baz is boolean[]
);
看起来不错!希望有帮助;祝你好运。