【发布时间】:2021-12-09 02:05:27
【问题描述】:
考虑以下类型:
type TComp <T> = (cb: (arg: T) => void, value: T) => void;
以及这种类型的两个实现:
const f1: TComp<number> = (cb: (a: number) => void, value: number) => {
cb(value + 122);
}
const f2: TComp<string> = (cb: (a: string) => void, value: string) => {
cb(value + "ok");
}
现在我介绍类型的字符串名称:
type TStrNum = 'str'|'num';
type TNameTypeMap = {
'str': string;
'num': number;
}
现在我想构造函数TComp <T> 这样:
const sf = <T extends TStrNum>(cb: (a: TNameTypeMap[T]) => void, value: TNameTypeMap[T], name: TStrNum) => {
if(name==='num') {
return f1(cb, value); //1 ts complains on `cb`
}
if(name==='str') {
return f2(cb, value); //2 ts complains on `cb`
}
}
//1TypeScript 抱怨:
Argument of type '(a: TNameTypeMap[T]) => void' is not assignable to parameter of type '(arg: number) => void'.
Types of parameters 'a' and 'arg' are incompatible.
Type 'number' is not assignable to type 'TNameTypeMap[T]'.
Type 'number' is not assignable to type 'never'.
在//2同样的错误,只抱怨字符串
看起来这个问题与cb 的逆变有关,但我仍然无法弄清楚到底是什么问题。
是否可以修复它或以其他方式实现此功能?
【问题讨论】:
-
this approach 适合您吗?一个小问题是
name: TStrNum而不是name: T,但即使您修复了编译器无法知道T不完全是TStrNum(请参阅ms/TS#27808)并且编译器也无法维护不同参数之间的相关性,除非我们将它们留在一个元组中充当discriminated union(尽管ms/TS#46266)如果合并可能会有所帮助)。 -
如果我在开头链接的代码适合您的用例,那么我可以写一个答案并解释正在发生的事情。如果没有,你能详细说明什么不起作用吗?
-
只需稍加修改,您的方法就适用于我——我只是将类型数组转换为对象,因此我可以不通过索引而是通过名称来引用值。请回答解释!
-
当然...如果您不介意更改调用者调用函数的方式,那么这样的更改是有意义的,因为您使用的是更明显的可区分联合类型。我不会把它放在我的答案中,因为它已经足够长了。