Discriminate Types
A discriminated type union is where you use code flow
analysis to reduce a set of potential objects down to one
specific object.
This pattern works really well for sets of similar
objects with a different string or number constant
for example: a list of named events, or versioned
sets of objects.
// When event comes into this function, it could be any
of the two potential types.
type TimingEvent = { name: "start"; userStarted: boolean } | { name: "closed"; duration: number };
// This pattern is the same with numbers which we can use
as the discriminator.
In this example, we have a discriminate union and an
additional error state to handle.
const handleEvent = (event: TimingEvent) => {
// By using a switch against event.name TypeScript's code
// flow analysis can determine that an object can only
// be represented by one type in the union.
switch (event.name) {
case "start":
// This means you can safely access userStarted
// because it's the only type inside TimingEvent
// where name is "start"
const initiatedByUser = event.userStarted;
break;
case "closed":
const timespan = event.duration;
break;
}
};
// You're better off using a switch statement instead of
if statements because you can make assurances that all
parts of the union are checked. There is a good pattern
for this using the never type in the handbook:
https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-never-type
type APIResponses = { version: 0; msg: string } | { version: 1; message: string; status: number } | { error: string };
const handleResponse = (response: APIResponses) => {
// Handle the error case, and then return
if ("error" in response) {
console.error(response.error);
return;
}
// TypeScript now knows that APIResponse cannot be
// the error type. If it were the error, the function
// would have returned. You can verify this by
// hovering over response below.
if (response.version === 0) {
console.log(response.msg);
} else if (response.version === 1) {
console.log(response.status, response.message);
}
};