Writing a Type-Level Switch Statement in TypeScript
by Peter Stuart on March 7, 2020
I recently came across a situation where I needed to write a very long conditional typeConditional types have the format T extends U ? X : Y
. Read more about them in the TypeScript docs.
that looked something like this:
Rather than nesting extends
expressions, which only supports a fixed number of conditions and results in a pyramid of doom in the definition of Foo
, this could be better expressed using a Switch
type:
type Switch<Value, [
, Result1],
[Match1, Result2],
[Match2, Result3],
[Match3...
> = ...
]
type Test1 = Switch<string, [
, "number"],
[number, "boolean"],
[boolean, "string"]
[string>;
]// Test1 = "string"
Switch
supports an arbitrary number of conditions, and we can write a recursive definition for it.
Defining Switch
To iterate through the array of conditions, I use the List.Head
and List.Tail
types from the excellent ts-toolbelt library:
import {List} from 'ts-toolbelt';
type Test1 = List.Head<[boolean, string, number]>;
// Test1 = boolean
type Test2 = List.Tail<[boolean, string, number]>;
// Test2 = [string, number]
Using Head
and Tail
, we can define Switch
like this:
type Switch<T, Conditions extends Array<[any, any]>> =
.Head<Conditions> extends never
List? never
: T extends List.Head<Conditions>[0]
? List.Head<Conditions>[1]
: Switch<T, List.Tail<Conditions>>;
We can confirm that it works with a few test types:
type Test1 = Switch<string, [
, "number"],
[number, "boolean"],
[boolean, "string"]
[string>;
]// Test1 = "string"
type Test2 = Switch<object, [
, "number"],
[number, "boolean"],
[boolean, "string"]
[string>;
]// Test2 = never
type Test3 = Switch<string, []>;
// Test3 = never
To add a default case, match against any
in the last condition:
type Test1 = Switch<string, [
, "number"],
[number, "default case"]
[any>;
]// Test1 = "default case"
For explanations of conditional types, implementing recursive types, and other advanced type techniques, check out these articles:
- Advanced Types in the TypeScript docs
- How to master advanced TypeScript patterns by Pierre-Antoine Mills (the author of ts-toolbelt)