1

I've got the following example code:

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);

Why do I get the error (line 6 on return func()):

Expected 1 arguments, but got 0.

I feel like GetMultipleFunction can be of either type 1 or type 2. Type 1 needs no arguments.

How do I need to change my code to get rid of the warning?

Typescript Playground

--

Edit: What I want to achieve:

I basically want a function that accepts a function as an argument. In my example test. And it should accept a function with an argument and a function without an argument. Is that even possible?

Code example:

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);

Typescript Playground

--

More specific explanation of what I want to achieve: I'm using Vue3 composition functions in order to extract Get requests. Usually I can just call: /api/groups/, but sometimes I need to pass an argument to my get request function: /api/groups/3/members/ Now number 3 would be needed to be passed as an argument.

6
  • 4
    Why multiple types at all? Just make data optional Commented Nov 13, 2021 at 11:49
  • Well I thought about that, but then i get another error: Type 'number | undefined' is not assignable to type 'number'. In the last line on test(getStrings). See: typescriptlang.org/play?#code/… Commented Nov 13, 2021 at 12:19
  • In general I'm fine with data being optional as long as no other errors show up. Commented Nov 13, 2021 at 12:20
  • 1
    What is the purpose of test? Right now it looks to me like the error is perfectly valid: You're calling func, but not passing in the data that func needs. So the two possible ways to fix it are to 1) pass in the data it needs, or 2) rewrite things so that it doesn't need data. But i don't know how to advise you on that since i don't understand the purpose of the code. Commented Nov 13, 2021 at 12:39
  • Make data optional in getStrings as well typescriptlang.org/play?#code/… Commented Nov 13, 2021 at 14:49

1 Answer 1

1

Problem is in this line type GetMultipleFunction = GetMultipleFunction1 | GetMultipleFunction2;.

Union of two functions never behave in a way you expect. It produces a function where argument is intersected type of both function arguments. See small example:

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()

This is by design an this is the safest way to call a union of functions.

In your example:

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

The safest way of calling func is providing an argument. It will cover two cases. If you passed a function where argument is not required - it will not make any harm.

I think in this example it worh using function intersection instead union. Like here:

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

However, we are getting error in a new place: test(getStrings); // error.

This is because Type '(data: number) => Promise<string[]>' is not assignable to type '() => Promise<string[]>'.

I is hard to make a guess what you are trying to achieve here. data argument in getStrings is unused.

You can also overload your test function:

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

Don't worry that I have used (...args: any[]), test function still accept only either GetMultipleFunction1 or GetMultipleFunction2

UPDATE

You can get rid of union function and overloadings at all. Just use rest parameters


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);

Playground

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for your answer. I've added a better explanation of what I want to achieve. My 2nd code example is basically the same as in my original code. The test func decides how func() is called. Maybe I should just make test1() or test2(). But I'm wondering if it's even possible what I want to achieve with a single function.
@danielmoessner made an update
I didn't know about Parameters<Fn> that is really interesting. And it works. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.