Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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!

>Solution :

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading