Reputation: 159
I receive a Typescript error that string
is not of type 'food' | 'drink' | other
, when I do:
order = x
Because Typescript assumes that x
can be any string, and not just 'food' | 'drink' | other
, so it throws an error.
Of course, I can get rid of this error if I put my code inside the type guard like this:
const order = 'food' // default value
if (x === 'food' || x === 'drink' || x === 'other') {
order = x
}
I just wonder, can I make this code shorter, DRY and non-repetitive?
Just to let you know, putting the strings in an array ['food', 'drink', 'other']
and doing the condition if (array.includes(x)) {order = x}
, doesn't do the trick!
Upvotes: 2
Views: 164
Reputation: 187144
You could use a type predicate function to cast this to the correct type:
function isMember<T extends string>(
array: readonly T[],
value: string
): value is T {
return (array as readonly string[]).includes(value)
}
This accepts an array, and a value. The member type of the array is captured as the generic parameter T
. The return value is value is T
, which means that if true
is returned, than the variable passed as value
will be considered a T
, and if false is returned then it will be treated as the type of the argument string
.
Which you could use like so:
const x: string = 'drink'
let order: 'food' | 'drink' | 'other' = 'food' // default value
if (isMember(['food', 'drink', 'other'], x)) {
order = x // x is of type: 'food' | 'drink' | 'other'
}
Now to clean this up a bit, you can setup your type and constants like so:
const yummyStrings = ['food', 'drink', 'other'] as const
type YummyString = (typeof yummyStrings)[number] // 'food' | 'drink' | 'other'
And use those like this:
const x: string = 'drink'
let order: YummyString = 'food' // default value
if (isMember(yummyStrings, x)) {
order = x // x is of type: 'food' | 'drink' | 'other'
}
Upvotes: 3