TypeScript type = foo | bar | baz ??? Union Types Demystified

At work this past Friday I found myself exposed, exposed as an imposter: a TypeScript imposter! Often times hang loose JS script kiddies like myself can manage to fumble or fake their way through working with statically typed enterpise grade TypeScript codebases if they have to, but this past Friday a single line of code brought me to my knees! But fret not my friend, if you happen to be stumped by the following line of code:

type Something = Foo | Bar | Baz;

I have your answer: Union Types

Let me explain. If you ever run into a mysterious line of code like the one above where a type equals one thing OR another thing OR something else entirely, you are looking at a TypeScript Union Type. So what is a Union Type? A Union Type is simply a type which can include several different types. Take an “Athlete” for example. One could create a union type called “Athlete” which could be used to represent several different sub-types such as “FootballPlayer”, “BasketballPlayer”, or “BaseballPlayer”. If for some reason you happen to encounter a union type in your company’s codebase that you need to work with, there are two main things you need to know about these types.

First, if you are working with an object with a union type, know that you will be able to access all of that objects properties which are shared among all of the different types which the union type unites. So if FootballPlayer, BasketballPlayer, and BaseballPlayer all have a shared set of properties, you can access those shared properties from your object with the type Athlete.

Second, you need to know that if you try to access a property of your Athlete object which is not shared among FootballPlayer, BasketballPlayer, and BaseballPlayer, your code will crash!

However, if you really need to access a property from your Athlete object that isn’t shared among those three types, you can cast the object as a specific type to access the unique property. I know that I may be loosing some of you right about now, so… without further ado, here is a nifty little example script demonstrating how to work with TypeScript Union Types if you ever find yourself in a tricky situation trying to juggle union types:

// DEMO: Using Union-Types in TypeScript
// Question: What are Union Types???
// Answer: Union Types are TypeScript types which can be equal to many different types which often share 'union' properties amongst the various different types. For example, an Athlete Union-Type could be used to represent FootballPlayer, BasketballPlayer, and BaseballPlayer types
type FootballPlayer = {
firstName: string,
lastName: string,
jerseyNumber: number,
sport: string,
touchdowns: number,
};
type BasketballPlayer = {
firstName: string,
lastName: string,
jerseyNumber: number,
sport: string,
points: number,
};
type BaseballPlayer = {
firstName: string,
lastName: string,
jerseyNumber: number,
sport: string,
hits: number,
};
type Athlete = BaseballPlayer | BasketballPlayer | FootballPlayer;
const quarterback: Athlete = {
firstName: "Ben",
lastName: "DiNucci",
jerseyNumber: 7,
sport: 'Football',
touchdowns: 0,
};
function displayStats(athlete: Athlete) {
switch(athlete.sport) {
case "Football":
// NOTE: We can access shared 'union' properties such as firstName,
// lastName, jerseyNumber, and sport using 'athlete' which is of the
// union type 'Athlete'
console.log("Player: " + athlete.firstName + " " + athlete.lastName);
// HOWEVER: If we want to access a non-union-type property such as
// touchdowns, we cannot use 'athlete' which is of the union type
// 'Athlete'. Attempting to call athlete.touchdowns will crash the app.
// Instead, we need to type-cast athlete as FootballPlayer if we wish
// to access the 'touchdowns' property
let footballPlayer: FootballPlayer = athlete as FootballPlayer;
console.log("Touchdowns: " + footballPlayer.touchdowns.toString());
break;
case "Basketball":
console.log("Player: " + athlete.firstName + " " + athlete.lastName);
let basketballPlayer: BasketballPlayer = athlete as BasketballPlayer;
console.log("Points: " + basketballPlayer.points.toString());
break;
case "Baseball":
console.log("Player: " + athlete.firstName + " " + athlete.lastName);
let baseballPlayer: BaseballPlayer = athlete as BaseballPlayer;
console.log("Hits: " + baseballPlayer.hits.toString());
break;
default:
console.log("Error: Something went wrong...");
}
}
displayStats(quarterback);
view raw unionTypes.ts hosted with ❤ by GitHub
 

topherPedersen