我正在使用定义了以下类型的库:
type VoidableCallback<EventValue> = EventValue extends void ? () => void : (val: EventValue) => void;
该库公开了一个返回上述类型的函数:
declare function fn<T>(): VoidableCallback<T>;
我想将此函数与有区别的联合使用:
type Action = { type: 'add'; n: number } | { type: 'multiply'; n: number };
const callback = fn<Action>();
callback({ type: 'add', n: 1 });
但是打字稿(3.4.1)给我这个错误消息:
类型'“ add”'不能分配给类型'“ add”&“ multiply”'。 ts(2322)
不能将类型“ add”分配给类型“ multiply”。预期类型来自属性'type',该属性在此处声明为类型'{type:“ add”; n:数字; }&{type:“ multiply”; n:数字; }'
我不明白为什么会这样-似乎总和(联合)类型被解释为“产品”类型。
如果我将类型定义更改为:
type VoidableCallback<EventValue> = (val: EventValue) => void;
...打字稿没有抱怨。因此,这与条件类型和联合类型有关。
如果可以理解这里发生的事情,那么也许我可以对图书馆(rxjs-hooks)进行公关。
答案 0 :(得分:2)
这是由条件类型的分布行为引起的。条件类型分布在裸类型参数上。这意味着,如果type参数包含联合,则条件类型将应用于联合的每个成员,结果将是所有应用程序的联合。因此,在您的情况下,我们将获得VoidableCallback<{ type: 'add'; n: number } | { type: 'multiply'; n: number }> = VoidableCallback<{ type: 'add'; n: number }> | VoidableCallback<{ type: 'multiply'; n: number }> = ((val: { type: 'add'; n: number }) => void) | ((val: { type: 'multiply'; n: number }) => void)
,您可以了解有关此行为here
得到交集错误的原因是打字稿处理函数签名的并集的方式,它基本上要求参数必须与联合中的所有签名兼容,因此参数必须是所有可能的参数类型的交集。您可以阅读有关此here
的信息简单的解决方案是禁用条件类型的分布。只需将type参数放在元组中即可轻松完成此操作:
type VoidableCallback<EventValue> = [EventValue] extends [void] ? () => void : (val: EventValue) => void;
type Action = { type: 'add'; n: number } | { type: 'multiply'; n: number };
declare function fn<T>(): VoidableCallback<T>;
const callback = fn<Action>();
callback({ type: 'add', n: 1 }); //ok now