dannyxnda
dannyxnda

Reputation: 1014

Typescript Array.prototype.find error with array type

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

Upvotes: 0

Views: 713

Answers (3)

jsejcksn
jsejcksn

Reputation: 33701

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

Upvotes: 0

philoj
philoj

Reputation: 488

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

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191976

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

Upvotes: 1

Related Questions