我想知道以下问题是否有解决方案:我有两个接受不同输入参数的函数,我有一个将每个函数映射到字符串的对象。
我定义了一种类型,该类型是对象的联合类型,其中包含映射中一个函数的键以及与函数参数类型匹配的值。给定具有该类型的对象,我想从map对象中检索相应的函数并使用值对其进行调用,但typescript不会进行类型推断。
const f1 = (value: number) => value;
const f2 = (value: string) => value;
const map = {
f1,
f2
};
type MyType = {
fun: "f1";
value: Parameters<typeof map["f1"]>;
} | {
fun: "f2";
value: Parameters<typeof map["f2"]>;
};
// does not work
const test1 = (b: MyType) => {
map[b.fun].apply(null, b.value);
};
// works
const test2 = (b: MyType) => {
if (b.fun === "f1") {
map[b.fun].apply(null, b.value);
} else {
map[b.fun].apply(null, b.value);
}
};
是否有可能做这样的事情而不必枚举开关中的所有可能性或if / else?
您遇到了TypeScript对我所说的correlated record types的不足支持。现在,它看到b.fun
是联合类型"f1" | "f2"
,并且b.value
是联合类型[string] | [number]
,并将其视为unorrelated。通常为a union of functions can only be called with an intersection of its parameters,在这种情况下为[string] & [number]
。由于无法将[string] | [number]
分配给[string] & [number]
,因此编译器会抱怨。实际上,没有值可分配给[string] & [number]
,因为它的第一个元素必须是与string & number
等效的never
。
为了使编译器验证map[b.fun].apply(null, b.value)
是类型安全的,它本质上需要多次分析代码,每种b
可能类型都需要分析一次。您可以通过在switch
/ case
或if
/ else
语句中复制代码来显式地实现此目的,但是它不会自动发生(这是有道理的,因为这将导致编译时间成倍增加)代码中的联合类型值的数量增加)和you can't even ask the compiler to do it on an opt-in basis。
目前,唯一合理的解决方法是,让您比编译器更了解代码的类型安全,并使用type assertion或等效代码告诉编译器不要担心,例如:
type MapVals = typeof map[keyof typeof map]; type LooselyTypedMapFunction = (...x: Parameters<MapVals>) => ReturnType<MapVals> const test1 = (b: MyType) => { (map[b.fun] as LooselyTypedMapFunction).apply(null, b.value); };
这里,类型
LooseLyTypedMapFunction
最终将f1
和f2
的类型模糊化为一个接受并产生string | number
的函数。然后,我们使用类型断言map[b.fun] as LooselyTypedMapFunction
对编译器说谎。我们告诉它map[b.fun]
将接受string
,并且还将接受number
。当然,这是错误的,但是只要您传递了相关的b.value
列表,而不是像`[Math.random()<0.5? “ oops”:123])。
该类型断言可能比您想要做的工作还要多,但是如果您将任何太疯狂的内容传递给map[b.fun]
,则至少它可能会捕获,因为[string] | [number]
至少会禁止[{foo: string}]
。您可以使用不太安全的断言,例如:
const test2 = (b: MyType) => { (map[b.fun] as Function).apply(null, b.value); };
这不需要
LooselyTypedMapFunction
的类型变乱,但也可以接受(map[b.fun] as Function).apply(null, [{foo: 123}]);
。所以要小心。
好的,希望能给您一些指导。祝你好运!