[英]Generic wrapper function that calls a method of an object by a given key
我想编写一个名为foo
的通用包装器 function,它采用 object o
和一个键k
加上一些args
,并以类型安全的方式调用o[k](...args)
并推断返回类型。
这是一个例子:
const obj = {
a: 1,
b: true,
c: (name: string) => `Hello, ${name}.`,
d: (name: string) => `Goodbye, ${name}.`,
};
const result = foo(obj, 'c', 'Peter');
// typeof result === 'string'
console.log(result); // Hello, Peter.
这是我到目前为止所做的:
function foo<
Args extends any[],
Return,
Target,
Key extends keyof {
[K in keyof Target as Target[K] extends ((...args: Args) => any) ? K : never]: Target[K]
}
>(target: Target, key: Key, ...args: Args): Return
{
// typeof method === 'Target[Key]'
const method = target[key];
// This expression is not callable. Type 'unknown' has no call signatures.ts(2349)
return method(...args);
}
// Argument of type 'string' is not assignable to parameter of type 'never'.ts(2345)
const result = foo(obj, 'c');
是否有可能在 TypeScript 中表达这样一个包装器 function 如果是这样,那么你将如何处理 go 呢?
编辑:
我接受了 Titian Cernicova-Dragomir 的回答并对其进行了调整以给我正确的返回类型:
type FunctionKeys<Target> = keyof {
[K in keyof Target as Target[K] extends ((...args: any) => any) ? K : never]: Target[K]
}
function foo<
Target extends Record<Key, (...args: any) => any>,
Key extends FunctionKeys<Target>,
>(...[target, key, ...args]: [Target, Key, ...Parameters<Target[Key]>]) {
const method = target[key];
return method(...args) as ReturnType<Target[Key]>;
}
我也可以调整 Tobias S。 以同样的方式回答,但我发现使用元组会产生更好的编译器错误。 这是一个例子:
const obj = {
greet: (name: string) => `Hello, ${name}.`,
};
// Expected 3 arguments, but got 2.ts(2554)
fooTitian(obj, 'greet');
// Argument of type '{ greet: (name: string) => string; }' is not assignable // to parameter of type 'Record<"greet", () => any>'.
// Types of property 'greet' are incompatible.
// Type '(name: string) => string' is not assignable to type '() => any'.ts(2345)
fooTobias(obj, 'greet');
您不需要Args
参数,只需使用Parameters
键入 rest 参数即可。 您还可以使用元组来说服 Typescript 参数是绑定在一起的
type FunctionKeys<Target> = keyof {
[K in keyof Target as Target[K] extends ((...args: any) => any) ? K : never]: Target[K]
}
function foo<
Target extends Record<Key, (...args: any) => any>,
Key extends FunctionKeys<Target>,
>(...[target, key, ...args]: [Target, Key, ...Parameters<Target[Key]>]): ReturnType<Target[Key]> {
// typeof method === 'Target[Key]'
const method = target[key];
return method(...args);
}
您可以像这样组合 function 的泛型类型:
function foo<
Target extends Record<Key, (...args: Args) => any>,
Args extends unknown[],
Key extends PropertyKey
>(target: Target, key: Key, ...args: Args): ReturnType<Target[Key]>
{
const method = target[key];
return method(...args);
}
魔法发生在Target
的约束中。 我们将其限制为Record
,其中键的类型为Key
。 值类型是 function,参数类型为Args
。 function 的返回类型是使用实用程序类型ReturnType
键入的。
为Arg
使用通用类型还允许在 function 实现中实现类型安全。 这将是错误的:
function foo<
Target extends Record<Key, (...args: Args) => any>,
Args extends unknown[],
Key extends PropertyKey
>(target: Target, key: Key, ...args: Args): ReturnType<Target[Key]> {
const method = target[key];
method("abc")
// ~~~~~ Argument of type '[string]' is not assignable to
// parameter of type 'Args'
return 123
// ~~~ Type 'number' is not assignable to type 'ReturnType<Target[Key]>'
}
这就是调用 function 的样子:
// valid
const result = foo(obj, "c", "Peter");
// invalid
foo(obj, "a")
foo(obj, "c", 123)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.