函数的打字稿联合/交集类型

Posted

技术标签:

【中文标题】函数的打字稿联合/交集类型【英文标题】:Typescript union/intersection types for functions 【发布时间】:2022-01-13 09:49:47 【问题描述】:

当我有不同数量的参数和返回类型时,我试图了解如何指定类型(对于函数)。

这个函数可以:

接受 2 个参数(一个是可选的)并返回一个 Promise 接受 2 个参数(第二个是回调) 接受 3 个参数(第三个是回调)

让我们首先为 3 种情况定义 3 种类型:

type Func1 = (a: string, b?: number) => Promise<void>;
type Func2 = (a: string, b: () => void) => void;
type Func3 = (a: string, c: number, b: () => void) => void;

现在我想定义一个包含这 3 个的类型。我读到您应该对函数类型使用交集。

type MultiFunction = Func1 & Func2 & Func3;

const f1: MultiFunction = async (a: string, b: number) => ;
const f2: MultiFunction = (a: string, b: () => void) => ;
const f3: MultiFunction = (a: string, c: number, b: () => void) => ;

f1("foo", 3);
f2("foo", () => );
f3("foo", 3, () => );

但这里的声明错误:

Type '(a: string, b: number) => Promise<void>' is not assignable to type 'MultiFunction'.
  Type '(a: string, b: number) => Promise<void>' is not assignable to type 'Func2'.
    Types of parameters 'b' and 'b' are incompatible.
      Type '() => void' is not assignable to type 'number'.ts(2322)

const f1: MultiFunction

如果我使用联合:

type MultiFunction = Func1 | Func2 | Func3;

const f1: MultiFunction = async (a: string, b: number) => ;
const f2: MultiFunction = (a: string, b: () => void) => ;
const f3: MultiFunction = (a: string, c: number, b: () => void) => ;

f1("foo", 3);
f2("foo", () => );
f3("foo", 3, () => );

f1("foo", 3); 出现错误:

Expected 3 arguments, but got 2.ts(2554)

types_test.ts(3, 37): An argument for 'b' was not provided.

我的问题是,处理这个(和类似的)案例的正确方法是什么?

【问题讨论】:

【参考方案1】:

像这样使用function overloading:

function func(a: string, b?: number):Promise<void>;
function func(a: string, b: () => void): void;
function func(a: string, c: number, b: () => void): void
function func(a: string, b?: any, c?: any): any 



func("foo", 3);
// interpreted as
// function func(a: string, b?: number | undefined): Promise<void>

func("foo", () => );
// interpreted as
// function func(a: string, b: () => void): void

func("foo", 3, () => );
// interpreted as
// function func(a: string, c: number, b: () => void): void

TS Playground 链接:https://tsplay.dev/WoJnLm

【讨论】:

这很有趣,但是你会被func 作为函数名卡住。我的目的是提供一种类型,以便我可以创建任意数量的具有不同名称和不同实现的函数,但仍具有所提供的签名之一。您可能已经注意到,签名是针对经典的二分法“承诺或回调”函数样式的。 这是实现“承诺或回调”模式的一种方式。如果我没记错的话,这是我在 Mongoose 中看到的。如果您传递回调,则调用回调并返回 void,如果不是返回的承诺。但我猜重载不是你想要的。 是的,函数重载不是我打算用于我的用例的。我想定义一个涵盖这些功能的泛型类型(泛型在这里不是正确的术语,但无论如何)。我想这要么是不可能的,要么就是太乱了。

以上是关于函数的打字稿联合/交集类型的主要内容,如果未能解决你的问题,请参考以下文章

从类型联合定义打字稿函数签名[重复]

打字稿:你如何用布尔或回调函数定义联合类型?

扩展排他联合的打字稿通用参数

无法让嵌套类型保护与打字稿中的联合类型一起使用

打字稿:将标记的联合转换为联合类型

打字稿:检查类型是不是为联合