For example, I have a literal string union type:
type AllowedColor = 'red' | 'blue';
And I'm receiving the color from the server.
let colors = getColorsFromServer();
I want to filter the array to only consist of AllowedColor
type. This is a pseudo code that is not working:
colors.filter(color => color is AllowedColor);
Is there a way to do this?
CodePudding user response:
Type aliases don't exist at runtime
You can't use a type alias in any runtime logic because of type erasure. type AllowedColor
will not exist in the generated Javascript.[1] Typescript types have only one role: static type checking logic.
You have to approach it from the other direction
Instead of runtime logic or data depending on static type information, have static type information depend on runtime data:
// values available at runtime
const allowedColors = ['red', 'blue', 'green'] as const
// type available at compile time, equivilent to
// type AllowedColor = 'red' | 'blue' | 'green'
type AllowedColor = typeof allowedColors[number]
let colors = ['red', 'blue', 'chartreuse', 'teal', 'ocher']
let filtered: AllowedColor[] =
colors.filter(color => allowedColors.includes(color as AllowedColor))
console.log(filtered) // [ 'red', 'blue' ]
If an allowedColors
enum
would be more useful than an array:
// values available at runtime
enum allowedColors {red = 'red', blue = 'blue', green = 'green'}
// type available at compile time, equivilent to
// type AllowedColor = 'red' | 'blue' | 'green'
type AllowedColor = typeof allowedColors[keyof typeof allowedColors]
let colors = ['red', 'blue', 'chartreuse', 'teal', 'ocher']
let filtered: AllowedColor[] =
colors.filter(color => color in allowedColors)
console.log(filtered) // [ 'red', 'blue' ]
CodePudding user response:
For now, my workaround for this is:
const allowedColors = ['red', 'blue'] as const;
type AllowedColor = typeof allowedColors[number];
let colors = getColorsFromServer();
colors.filter(color => allowedColors.includes(color));
Let me know if you know better approach
CodePudding user response:
It worth using typeguard, since Array.prototype.filter
expects a predicate as an argument.
Consider this example:
type AllowedColor = 'red' | 'blue';
const getColorsFromServer = () => {
let color: string[] = ['green', 'red', 'blue']
return color;
}
let colors = getColorsFromServer();
const isAllowed = (color: string): color is AllowedColor => /red|blue/.test(color)
// const isAllowed = (color: string): color is AllowedColor => ['red', 'blue'].includes(color)
const result = colors.filter(isAllowed) // AllowedColor[]