I have code like this:
type A = {
test: number;
a: number;
}[];
type B = {
test: number;
b: number;
}[];
type C = {
test: number;
c: number;
}[];
export const test = (arg: A | B | C) => {
return arg.find((e: (A | B | C)[number]) => e.test === 1);
// ~~~~
// Error 2349
};
In VSCode the find
method is underlined with error:
This expression is not callable.
Each member of the union type '{ <S extends { test: number; a: number; }>(predicate: (this: void, value: { test: number; a: number; }, index: number, obj: { test: number; a: number; }[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: { ...; }, index: number, obj: { ...; }[]) => unknown, thisArg?: any): { ...; } | undefined; } |...' has signatures, but none of those signatures are compatible with each other. (2349)
Why does this happen?
Link to code in TypeScript Playground
CodePudding user response:
It's because you have 3 conflicting signatures, and TS needs some help to find the common ground - test
.
You can create a base interface with the test
property, and then extend the object's types from the Base
interface. Now you can use generics, and define that the test
function accepts any object that extends Base
(TS playground):
interface Base {
test: number
}
interface A extends Base {
a: number
}
interface B extends Base {
b: number
}
interface C extends Base {
c: number
}
export const test = <T extends Base>(arg: T[]) => {
return arg.find((e: T) => e.test === 1)
}
CodePudding user response:
Apart from the signature mismatch, there is a slight mistake in the line
return arg.find((e: (A | B | C)[number]) => e.test === 1)
e
is not A|B|C
because A,B,C are array types.
I think what you need is something like:
type A = {
test: number
a: number
}
type B = {
test: number
b: number
}
type C = {
test: number
c: number
}
export const test = (arg: (A|B|C)[]) => {
return arg.find((e: (A | B | C)) => e.test === 1)
}
Signature mismatch occurs on the return type of find()
. The return type of T[].find()
is T|undefined
. In this case, it should be (A|B|C)|undefined
. This is why (A|B|C)[]
works, then the return type becomes (A|B|C)|undefined
.
Playground
CodePudding user response:
If you modify your types so that they represent the object members of the arrays, you can rewrite the function signature to indicate that the argument is an array of the union of them all, then TypeScript will have no problem inferring that the test
property is common to all types in the union:
type A = {
test: number;
a: number;
};
type B = {
test: number;
b: number;
};
type C = {
test: number;
c: number;
};
export const test = (array: (A | B | C)[]) => {
return array.find(element => element.test === 1);
};