DEV Community

Cover image for How to check if string is member of union type
Hans Ott (journy.io)
Hans Ott (journy.io)

Posted on • Edited on

How to check if string is member of union type

There's some ongoing debate whether enum in TypeScript should be used: The Dangers of TypeScript Enums

A TypeScript enum is compiled to an object at runtime:

enum Suits {
  Hearts = "hearts",
  Diamonds = "diamonds",
  Spages = "spades",
  Clubs = "clubs"
}
Enter fullscreen mode Exit fullscreen mode
var Suits;
(function (Suits) {
    Suits["Hearts"] = "hearts";
    Suits["Diamonds"] = "diamonds";
    Suits["Spages"] = "spades";
    Suits["Clubs"] = "clubs";
})(Suits || (Suits = {}));
Enter fullscreen mode Exit fullscreen mode

One benefit of this approach is being able to check at runtime whether a string is a member of the enum:

function isSuit(value: string): value is Suits {
  return Object.values(Suits).includes(value)
}
Enter fullscreen mode Exit fullscreen mode

The alternative for enum is using a union type:

type Suit = "hearts" | "diamonds" | "spades" | "clubs"
Enter fullscreen mode Exit fullscreen mode

Type information doesn't exist at runtime, how can we check at runtime if a string is a member of the union type?

function isSuit(value: string): value is Suit {
  return ["hearts", "diamonds", "spades", "clubs"].includes(value as Suit)
}
Enter fullscreen mode Exit fullscreen mode

This works but there is an issue with this approach... When you add or remove members, we need to update the array. This is prone to errors because we might forget to update the array or the type. How can we do this automatically?

const ALL_SUITS = ['hearts', 'diamonds', 'spades', 'clubs'] as const;
type SuitTuple = typeof ALL_SUITS;
type Suit = SuitTuple[number];

function isSuit(value: string): value is Suit {
  return ALL_SUITS.includes(value as Suit)
}
Enter fullscreen mode Exit fullscreen mode

We define the possible options using an array with a const assertion. A const assertion tells the compiler to infer the narrowest or most specific type it can for an expression. If you leave it off, the compiler will use its default type inference behavior, which will possibly result in a wider or more general type.

This gets compiled to

const ALL_SUITS = ['hearts', 'diamonds', 'spades', 'clubs'];

function isSuit(suit) {
    return ALL_SUITS.includes(suit);
}
Enter fullscreen mode Exit fullscreen mode

Which is a lot nicer than using an enum! 🀩

Let me know if this blogpost was useful! 😊

Top comments (3)

Collapse
 
vasylnahuliak profile image
Vasyl Nahuliak • Edited

I guess in enum case have a bug. Need to use Object.keys instead Object.values

dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
hansott profile image
Hans Ott (journy.io)

Oh, you're right. Thanks for spotting! I'll update the blogpost.

Collapse
 
kirkcodes profile image
Kirk Shillingford

I found this very useful! Thank you so much for the explanation!