'Array of union type errors using includes
Given the following:
type Arr = (0|1)[]
const arr: Arr = [0, 1]
const someNumber: number = 5;
arr.includes(someNumber);
// ^^^^^^^^^^
// Argument of type 'number' is not assignable to parameter of type '0 | 1'.
Is there a way to make arr.includes(someNumber) work for TypeScript without changing the definition of Arr or doing some type casting?
Doesn't this error defeat the purpose of using Array.prototype.includes()?
The
includes()method determines whether an array includes a certain value among its entries, returningtrueorfalseas appropriate.
Solution 1:[1]
Doesn't this error defeat the purpose of using Array.prototype.includes()?
Not really. It's just TypeScript doing its job: making the code typesafe. You've said that arr can only include the values 0 or 1, so calling includes with a value that can't be in the array is a type error. (That said, I could definitely see an argument for includes to accept number instead of 0 | 1 in a case like that. I suspect there are counter-arguments though.)
You could write a utility function to check if a number is a valid element for arr:
const isValidArrElement = (value: number): value is Arr[number] => {
return value === 0 || value === 1;
};
Then use that before using includes:
const arr: Arr = [0, 1]
const someNumber: number = 5;
if (isValidArrElement(someNumber) && arr.includes(someNumber)) {
// ...
}
But I usually prefer to approach this the other way so that I'm not writing the 0 and 1 in two places and creating a maintenance hazard:
const validArrElements = [0, 1] as const;
type ArrElement = (typeof validArrElements)[number];
// ^? -- type is 0 | 1
const isValidArrElement = (value: number): value is ArrElement => {
return (validArrElements as readonly number[]).includes(value);
};
Note that isValidArrElement does have a type assertion in it. I'm not bothered about that, because I only do this in these pairs of things (constant arrays of valid values and type checkers for them).
The usage is the same as above:
const arr: Arr = [0, 1]
const someNumber: number = 5;
if (isValidArrElement(someNumber) && arr.includes(someNumber)) {
// ...
}
Solution 2:[2]
You defined a union type consisting of 0 | 1 which means your array values will be type-guarded with value 0 or 1. That's how union types work in Typescript.
The usual array will be defined with a primitive type like below
const arr: Array<number> = [0, 1]
That is one of choices
But if you still want to go with your union type definition, you can cast it to number[] which allows all numbers being used in array's functions
type Arr = (0|1)[]
const arr: Arr = [0, 1]
const someNumber: number = 5;
(arr as readonly number[]).includes(someNumber) //correct
const someString: string = "5";
(arr as readonly number[]).includes(someString) //error
Solution 3:[3]
let scores : (string | number)[];
scores = ['Programming', 5, 'Software Design', 4];
const someNumber: number = 5
console.log('scores.includes(someNumber)', scores.includes(someNumber))
It's returing True
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | T.J. Crowder |
| Solution 2 | Nick Vu |
| Solution 3 | piash tanjin |
