Often we’ll want to check if an array contains some value, like so:
const PRETTY_COLORS = ['pink', 'orange', 'yellow'] as const;
const handleColor = (color: string) => {
if (PRETTY_COLORS.some(c => c === color)) {
// `color` is typed as a string, but we know it must be one of the values
// in the readonly array above. It should be a narrower type.
console.log(color);
}
}
Unfortunately, TypeScript doesn’t appropriately narrow the type of the value within the if
block. It continues to treat the value like a string
. We can write a helper function that gets us what we want.
const PRETTY_COLORS = ['pink', 'orange', 'yellow'] as const;
function isInList<T extends readonly any[]>(list: T, element: any): element is T[number] {
return list.includes(element as any);
}
const handleColorTypeSafe = (color: string) => {
if (isInList(PRETTY_COLORS, color)) {
// color: 'pink' | 'orange' | 'yellow'
console.log(color);
}
}
Now, color
is appropriately narrowed in type when we’re within the if
block.
To break down our helper function a bit, which is known as a type guard:
element is T[number]
is saying that the element must exist within the array.T extends readonly any[]
is enforcing thatT
is some array