Home > Enterprise >  Array of union type errors using includes
Array of union type errors using includes

Time:05-01

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'.

Playground

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, returning true or false as appropriate.

CodePudding user response:

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)) {
    // ...
}

Playground link

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)) {
    // ...
}

Playground link

CodePudding user response:

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

Playground

CodePudding user response:

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

  • Related