Manzana
Manzana

Reputation: 159

Typescript type guards for string union types without repetition

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

Answers (1)

Alex Wayne
Alex Wayne

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

Playground


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

Playground

Upvotes: 3

Related Questions