Currently working with React and TypeScript. I've noticed that when iterating through an array using map(), the type checks are not always consistent. For instance, in the code snippet below, I mistakenly pass a string "more" into the eachItem function where it expects a number. However, no error is thrown:
function eachItem(val: number, i: number) {
return Number(val) / 2;
}
const arr = [4, "more", 6];
arr.map(eachItem);
I believe I understand why this behavior occurs, but my concern is how to enforce strict typing on a map function so that an error is triggered when passing in a string:
Argument of type 'string' is not assignable to parameter of type 'number'
The reason no error is raised is due to supplying a callback to map(). The map() function expects a callback with this signature:
'(value: string | number, index: number, array: (string | number)[]) => Element'
However, I am providing a callback with this signature:
'(item: number, i: number) => Element'
Instead of checking if 'number' contains 'string | number', TypeScript checks if 'string | number' includes 'number' and deems it true. While my logic warrants an error since I am passing "more" where only numbers are allowed, TypeScript's logic sees no issue as I am passing a function that accepts numbers where functions that accept strings and numbers are permissible. This may seem insignificant, but errors can arise unexpectedly when dealing with union types. The following example illustrates the resulting error:
interface IMoney {
cash: number;
}
interface ICard {
cardtype: string;
cardnumber: string;
}
type IMix = IMoney | ICard;
const menu = [
{cash: 566},
{cardtype: "credit", cardnumber: "111111111111111111"}
];
menu.map((item: IMix, i: number) => (item.cash || item.cardtype));
Property 'cash' does not exist on type 'IMix'.
Property 'cash' does not exist on type 'ICard'.
To address this issue, I now have to do the following, albeit sacrificing clarity regarding the mutual exclusivity of 'cash' and 'cardtype' within my code:
interface IMix {
cash?: number;
cardtype?: string;
cardnumber?: string;
}