我正在使用定义了以下类型的库:
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 });
但是 Typescript (3.4.1) 给我这个错误信息:
Type '"add"' is not assignable to type '"add" & "multiply"'. Type '"add"' is not assignable to type '"multiply"'.ts(2322)
The expected type comes from property 'type' which is declared here on type '{ type: "add"; n: number; } & { type: "multiply"; n: number; }'
我不明白这是为什么 - 总和(联合)类型似乎被解释为“产品”类型。
如果我将类型定义更改为:
type VoidableCallback<EventValue> = (val: EventValue) => void;
... Typescript 不会提示。所以这与条件类型和联合类型有关。
如果能理解这里发生的事情,那么也许我可以向图书馆 (rxjs-hooks) 发起 PR。
最佳答案
这是由条件类型的分布行为引起的。条件类型分布在裸类型参数上。这意味着如果类型参数包含联合,则条件类型将应用于联合的每个成员,结果将是所有应用程序的联合。所以在你的情况下我们会得到 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
交集错误的原因是 typescript 处理函数签名联合的方式,它基本上要求参数与联合中的所有签名兼容,因此参数必须是所有可能参数类型的交集.你可以阅读这个 here
简单的解决方案是禁用条件类型的分发。这很容易通过将类型参数放在元组中来完成:
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
关于具有可区分联合的 typescript 条件类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55542332/