具有可区分联合的 typescript 条件类型

标签 typescript

我正在使用定义了以下类型的库:

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

Playground link

关于具有可区分联合的 typescript 条件类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55542332/

相关文章:

javascript - this 内部原型(prototype)函数等于 window 而不是对象实例

javascript - 在提交表单上捕获 500 代码

javascript - 在 Fetch 中返回 Promise.Reject()

javascript - TypeScript 匿名函数

javascript - 使用 Angular 8 响应式表单捕获表单输入

typescript 检查值在函数体中仍然标记为未定义

typescript - 为什么我在 Cypress 中收到错误 `TypeError: fs.readdir is not a function`

angular - 错误加载包 '' : Every . bzl 文件必须有对应的包,但是 '@npm//:install_bazel_dependencies.bzl' 没有

javascript - 具有路径和 equalTo bool 值的 Firebase orderByChild 不会返回任何内容

function - typescript 接口(interface) : function as property - or - this in interface implementations