Typescript: Why does array includes on an array union type not work?

Viewed 52

I have the following type:

type Test = {
  roles: ["nonProfit"];
  nonProfitPrivateDetails: NonProfitPrivateDetails;
} | {
  roles: ["artist"];
  artistPrivateDetails: ArtistPrivateDetails;
} | {
  roles: ["artist", "nonProfit"];
  nonProfitPrivateDetails: NonProfitPrivateDetails;
  artistPrivateDetails: ArtistPrivateDetails;
}

However, when I try to use Array includes on it, the type isn't being inferred:

const t: Test = null as any
t.roles.includes("artist") // fails to compile. 

It fails because "Argument of type 'string' is not assignable to parameter of type 'never'".

IntelliSence is showing includes as a type of: Array<T>.includes(searchElement: never, fromIndex: number | undefined): boolean.

I think I'm just going to have to write custom type guards instead of using includes, but I'd like to understand why this doesn't work.

Thanks!

1 Answers

A union of functions or like here a method on a union of type, is only safe to invoke it with an intesection of parameters which in this case resolves to never.

Since roles: ["nonProfit"] | ["artist"] | ["artist", "nonProfit"], there is no intersection hence the never.

One possible fix for that, make the role an enum / a type :

type Role = "nonProfit" | "artist"

type Test = {
  roles: Array<Role>;
  nonProfitPrivateDetails: NonProfitPrivateDetails;
} | {
  roles: Array<Role>;
  artistPrivateDetails: ArtistPrivateDetails;
} | {
  roles: Array<Role>;
  nonProfitPrivateDetails: NonProfitPrivateDetails;
  artistPrivateDetails: ArtistPrivateDetails;
}

declare const t: Test;
t.roles.includes("artist") // ok
Related