Home > Back-end >  Typescript Array.prototype.find error with array type
Typescript Array.prototype.find error with array type

Time:03-09

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:

TS Playground

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

  • Related