Skip to content

Instantly share code, notes, and snippets.

@robinpokorny
Last active January 23, 2022 12:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robinpokorny/9e52e4c4d4801297099d6f0620ff2012 to your computer and use it in GitHub Desktop.
Save robinpokorny/9e52e4c4d4801297099d6f0620ff2012 to your computer and use it in GitHub Desktop.
Tagged Unions in TypeScript Showcase
// And you will know my name is TypeScript
// When I lay my Tagged Union upon thee
// by @robinpokorny
// 1. Union of literals
// The cornerstone of any nutritious breakfast.
type Burger1 = `McDonalds` | `BigKahuna`;
const brettsBreakfast1_1: Burger1 = `BigKahuna`;
const brettsBreakfast1_2: Burger1 = `Whooper`; // Caught
// 2. Mixed approach
// Let's say BigKahuna Burgers are numbered
type Burger2 = {
brand: `McDonalds` | `BigKahuna`;
name: `Le Big Mac` | `1/4 pounder` | `Cheese Royal` | number;
};
const brettsBreakfast2_1: Burger2 = {
brand: `McDonalds`,
name: `Le Big Mac`,
};
const brettsBreakfast2_2: Burger2 = {
brand: `BigKahuna`,
name: `Le Big Mac`, // Mother…
};
// 3. Tagged union
type Burger =
| {
brand: `McDonalds`;
name: `Le Big Mac` | `1/4 pounder` | `Cheese Royal`;
}
| {
brand: `BigKahuna`;
name: number;
};
const brettsBreakfast3_1: Burger = {
brand: `McDonalds`,
name: `Le Big Mac`, // Fine
};
const brettsBreakfast3_2: Burger = {
brand: `BigKahuna`,
name: `Le Big Mac`, // Caught
};
const prepare = (burger: Burger) => {
if (burger.brand === `McDonalds`) return mcPrepare(burger);
if (burger.brand === `BigKahuna`) return bkPrepare(burger);
return burger.brand; // Unreachable
};
// 4. One from the union
const mcPrepare = (burger: McBurger) => burger.name;
// How to define McBurger?
type McBurger = Extract<Burger, { brand: `McDonalds` }>;
// 5. Favorites at each brand
const brettsFavorites: Favorites = {
McDonalds: `Le Big Mac`,
BigKahuna: 17,
};
// How to define Favorites in sync with Burger?
type Favorites1 = {
[B: string]: Burger[`name`];
};
const brettsFavorites1: Favorites1 = {
BigKahuna: 17,
// Mother…
};
// 6. Intoducing mapped types
type Favorites2 = {
[B in Burger[`brand`]]: Burger[`name`];
};
const brettsFavorites2: Favorites2 = {
McDonalds: 25, // Mother…
BigKahuna: 17,
};
type Favorites3 = {
[B in Burger[`brand`]]: Extract<Burger, { brand: B }>[`name`];
};
const brettsFavorites3: Favorites3 = {
McDonalds: 25, // Caught
BigKahuna: 17,
};
// 7. Key remapping
// (since TS 4.1)
type Favorites = {
[B in Burger as B[`brand`]]: B[`name`];
};
// Other things you can do
type Allow = {
[B in Burger[`brand`] as `allow${Uppercase<B>}`]: boolean;
};
const allowed: Allow = {
allowMCDONALDS: true,
allowBIGKAHUNA: false,
};
// UTILS
export const bkPrepare = (burger: Burger) => burger.name;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment