我想创建一个函数来处理调用其他函数。这些其他函数应该触发类似的业务和清理逻辑。
function foo(arg: number) {
// do something with arg
}
function bar(arg: string) {
// do something with arg
}
const FUNCTIONS = {
foo,
bar,
} as const;
type FnType = keyof typeof FUNCTIONS;
type FnArg<Type extends FnType> = Parameters<typeof FUNCTIONS[Type]>[0];
function callFunction<Type extends FnType>(type: Type, arg: FnArg<Type>) {
// some business logic here that is shared by all functions
const fn = FUNCTIONS[type];
// the type of fn and arg are both derived from Type:
// - 'typeof fn' would be 'typeof Functions[Type]'
// - 'typeof args' would be 'FnArg<Type>'
// However, TS seems to see these 2 types as independent and cannot
// figure out that fn and arg can work together.
return fn(arg); // -> TS doesn't 'know' that arg should have the right type although we know that's the case thanks to generics
}
// generics make sure the second argument is of the right type
callFunction('foo', 5);
callFunction('foo', 'arg'); // errors as expected
callFunction('bar', 'arg');
callFunction('bar', 5); // erros as expected
我能够使用泛型来确保TS检查将被代理到这些函数的参数是正确的类型。然而,在函数实现中,TS似乎并不知道泛型将确保参数是正确的类型。
你知道有没有办法让TS明白调用fn(arg)
就可以了吗?
1条答案
按热度按时间h79rfbju1#
正如您所看到的,编译器无法看到
FUNCTIONS[type]
类型和arg
类型之间的相关性。这个问题本质上与microsoft/TypeScript#30581中描述的相同,在microsoft/TypeScript#30581中,它是按照相关的联合类型来表述的。除非你非常小心,否则当编译器试图调用FUNCTIONS[type](arg)
时,它会将FUNCTIONS[type]
的generic类型扩展为并集((arg: string) => void) | (arg: number) => void))
,而arg
类型扩展为string | number
,不幸的是,你不能用后者的参数调用前者。实际上,编译器将函数的并集折叠成其参数的交集的单个函数(如TS 3的发行说明中所述)。3)你得到了(arg: never) => void
,它根本不能被调用,因为它需要一个不可能的never
类型的参数;不能同时是string
和number
。所以如果你这样做,你就被卡住了:幸运的是,有一种方法可以修复它,如microsoft/TypeScript#47109中所述。其思想是重构您的类型,以便将它们显式地作为泛型indexed accesses上的操作写入mapped types,而不是简单或“基本”对象类型。关键是,您希望
fn
被视为具有泛型类型(arg: XXX) => void
,并且您希望arg
被视为相同泛型XXX
的类型XXX
。因此FUNCTIONS
的类型必须更改为Map到该基类型。这里有一个方法。首先,我们将
FUNCTIONS
变量重命名为:然后我们使用它来构造基本对象类型:
然后我们声明
FUNCTIONS
是FnArg
上的Map类型:现在你可以将
callFunction()
写为对通用索引访问进行操作:就这样!
请注意,
FUNCTIONS
的类型和_FUNCTIONS
的类型是等效的,特别是当您要求IntelliSense显示它们时:它们之间的唯一区别是它们在内部的表示方式,Map类型
FUNCTIONS
允许编译器看到FUNCTIONS[type]
和arg
之间的相关性。如果你在callFuction()
的主体中用_FUNCTIONS
替换FUNCTIONS
,你原来的错误会马上回来。因此,这个问题是一个微妙的问题,我在这里所能做的就是向人们指出Microsoft/TypeScript#47109,以获得这项技术的源代码,在那里实现者解释它是如何工作的以及为什么工作的。
Playground链接到代码