首页 > 解决方案 > 打字稿:当函数可以是多种函数类型时,函数参数出错

问题描述

我有以下示例代码:

type GetMultipleFunction1 = () => Promise<string[]>; 
type GetMultipleFunction2 = (data: number) => Promise<string[]>; 
type GetMultipleFunction = GetMultipleFunction1 | GetMultipleFunction2;

function test(func: GetMultipleFunction): Promise<string[]> {
    return func(); 
}

function getStrings(data: number): Promise<string[]> {
    return new Promise(function (resolve, reject) {
        resolve(['hello', 'hi']);
    }) }

test(getStrings);

为什么我会收到错误消息(第 6 行return func()):

预期 1 个参数,但得到 0。

我觉得 GetMultipleFunction 可以是类型 1 或类型 2。类型 1 不需要参数。

我需要如何更改代码以消除警告?

打字稿游乐场

--

编辑:我想要实现的目标:

我基本上想要一个接受函数作为参数的函数。在我的例子test中。它应该接受一个带参数的函数和一个不带参数的函数。这甚至可能吗?

代码示例:

type GetMultipleFunction1 = () => string[];
type GetMultipleFunction2 = (data: number) => string[];
export type GetMultipleFunction = GetMultipleFunction1 | GetMultipleFunction2;

function test(func: GetMultipleFunction, data?: number): string[] {
    if (data) return func(data);
    return func()  // here should be no warning
}

function getStrings1(data: number): string[] {
    return [`hello-${data}`, 'hi'];
}

function getStrings2(): string[] {
    return ['hello', 'hi']
}

test(getStrings1, 2);
test(getStrings2);

打字稿游乐场

--

我想要实现的更具体的解释:我正在使用 Vue3 组合函数来提取 Get 请求。通常我可以只调用: /api/groups/,但有时我需要将参数传递给我的 get 请求函数:/api/groups/3/members/现在需要将数字 3 作为参数传递。

标签: typescript

解决方案


问题出在这一行type GetMultipleFunction = GetMultipleFunction1 | GetMultipleFunction2;

两个函数的联合永远不会以您期望的方式运行。它产生一个函数,其中参数是两个函数参数的相交类型。看小例子:

type Foo = (arg: { age: number }) => void
type Bar = (arg: { name: string }) => void

type Union = Foo | Bar

declare let func:Union

// let func: (arg: { age: number;} & { name: string;}) => void
func()

这是设计使然,这是调用函数联合的最安全方式。

在您的示例中:

function test(func: GetMultipleFunction): Promise<string[]> {
    return func(); // < ------ ERROR
}

最安全的调用方式func是提供参数。它将涵盖两种情况。如果您传递了一个不需要参数的函数 - 它不会造成任何伤害。

我认为在这个例子中它使用函数交集而不是联合。像这儿:

type GetMultipleFunction1 = () => Promise<string[]>;
type GetMultipleFunction2 = (data: number) => Promise<string[]>;
export type GetMultipleFunction = GetMultipleFunction1 & GetMultipleFunction2;

function test(func: GetMultipleFunction): Promise<string[]> {
    return func(); // no error
}

function getStrings(data: number): Promise<string[]> {
    return new Promise(function (resolve, reject) {
        resolve(['hello', 'hi']);
    })
}

test(getStrings); // error

但是,我们在一个新的地方遇到了错误:test(getStrings); // error.

这是因为Type '(data: number) => Promise<string[]>' is not assignable to type '() => Promise<string[]>'.

我很难猜出你想在这里实现什么。data参数 ingetStrings未使用。

你也可以重载你的test函数:

type GetMultipleFunction1 = () => Promise<string[]>;
type GetMultipleFunction2 = (data: number) => Promise<string[]>;

function test(func: GetMultipleFunction2): Promise<string[]>
function test(func: GetMultipleFunction1): Promise<string[]>
function test(func: (...args: any[]) => Promise<string[]>): Promise<string[]> {
    return func(); // ok
}

function getStrings(data: number): Promise<string[]> {
    return new Promise(function (resolve, reject) {
        resolve(['hello', 'hi']);
    })
}

test(getStrings); // ok

test(() => Promise.resolve(42)); // expected error

不用担心我已经使用了(...args: any[]),测试功能仍然只接受 GetMultipleFunction1要么GetMultipleFunction2

更新

您可以完全摆脱联合函数和重载。只需使用休息参数


function test<Fn extends (...args: any[]) => string[]>(func: Fn, ...params: Parameters<Fn>): string[] {
    return func(...params)  // ok
}

function getStrings1<Data extends number>(data: Data) {
    return [`hello-${data}`, 'hi'];
}

function getStrings2() {
    return ['hello', 'hi']
}

test(getStrings1, 2);
test(getStrings1); // expected error
test(getStrings2);

操场


推荐阅读