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, [
[Match1, Result1],
[Match2, Result2],
[Match3, Result3],
...
]> = ...
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]>> =
List.Head<Conditions> extends never
? 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 = neverTo add a default case, match against any in the last condition:
type Test1 = Switch<string, [
[number, "number"],
[any, "default case"]
]>;
// 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)