I should be allowed to pass anything into Array.includes to check if it's in the array, but TypeScript doesn't want me to pass in something that isn't the right type. For instance:
type Fruit = "apple" | "orange";
type Food = Fruit | "potato";
const fruits: Fruit[] = ["apple", "orange"];
function isFruit(thing: Food) {
return fruits.includes(thing); // ts error: "potato" is not assignable to type 'Fruit'.
}
Playground
What is a clean way to fix this code with minimal impact on readability?
First, please read this QA from TypeScript 3.x days where someone was asking essentially the same question as yourself: TypeScript const assertions: how to use Array.prototype.includes?
Now, in your case, as an alternative to @CertainPerformance's suggestion of unknown[] (which loses type information), you can legally widen fruits to readonly string[] (without using as), which is compatible with Fruit and Food:
type Fruit = "apple" | "orange";
type Food = Fruit | "potato" | "egg";
const fruits: readonly Fruit[] = ["apple", "orange"];
function isFruit(food: Food): food is Fruit {
const fruitsAsStrings: readonly string[] = fruits;
return fruitsAsStrings.includes(food);
}
An alternative, (theoretically more "correct") approach is to add a variant includes member to the ReadonlyArray<T> interface (as suggested in the linked QA) which allows U to be a supertype of T instead of the other way around.
interface ReadonlyArray<T> {
includes<U>(x: U & ((T & U) extends never ? never : unknown)): boolean;
}
type Fruit = "apple" | "orange";
type Food = Fruit | "potato" | "egg";
const fruits: readonly Fruit[] = ["apple", "orange"];
function isFruit(food: Food): food is Fruit {
return fruits.includes(food);
}
Having said all of that... if you intend to use a collection-type as a value/type set-membership test, you should use a JavaScript Set<T> (or use object keys) instead of an Array: not only because of performance reasons (as both Set<T>.has() and object key lookup are O(1) but Array.includes() is O(n) - and also because TypeScript works better with keyof types.
...implementing that is an exercise for the reader.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With