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?
--
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);
--
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.
CodePudding user response:
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);