TypeScript Recursive Type Aliases Explained
A Deep Dive into TypeScript Recursive Type Aliases
Before TypeScript 3.7, a recursive type reference will cause the TypeScript compiler to throw a circular references error message. Developers must make a workaround (i.e., using an interface) to achieve a recursive reference.
The recursive type has been introduced since TypeScript 3.7. It allows the reference to the type from its own definition by deferring the type reference until the instantiating .
As recursion is a common programming pattern, there are many use cases in which a recursive type is very useful. Using recursive type, we can concisely represent a complex data structure.
In this article, I will explore how to use recursive type aliases in TypeScript.
Use recursive type to represent a data structure.
The recursive type can be used to represent a data type that has a nested structure. Below is an example:
1 | const myData = { |
This data type can be abstracted as a stack data type.
1 | type Stack<T> = { |
The above code defines a generic data type Stack
. It is composed of a top
property of type T
, and a rest
property that is of the same Stack
type inside the type definition.
Another good example is the JSON data type. In the recursive type TypeScript playground example, the following code snippet is used to define a JSON type:
1 | type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; |
The Json
type works just like our previous Stack
type, and we are using the Json
type alias to represent the nested JSON child nodes.
As shown in the above examples, using recursive type makes the type definition cleaner and more readable, as the type definition matches the recursive nature of a data structure.
Limitation of Recursive Type
The recursive type aliases in TypeScript have the limitation of not allowing immediate “self-instantiation”. Below is an example:
1 | type Stack<T> = { |
In this case, the TypeScript compiler throws an error: “_Type alias ‘Stack1’ circularly references itself. (2456)_”. The restriction is reasonable, as the immediate self-reference in the above example will cause infinity recursion in compile time.
Advanced Usage
We can achieve some complex type operations by combining recursive type aliases with other advanced type features (i.e., Conditional Type).
Let’s say we have a Client
Type that represents client data.
1 | type Client = { |
We aim to define a PropertyType
property type that can extract from the nested type structure.
1 | type postCode = PropertyType<Client, 'address.suburb.postCode'>; |
The type should be able to take two arguments, a generic type argument, and a property path string, to locate the property. If the property path doesn’t exist, then return thenever
type.
Recursive type aliases fit this situation well, enabling us to traverse through the object type structure.
Below is the implementation of the PropertyType
1 | type PropertyType<T, Path extends string> = |
Although the type is just a one-liner, it makes use of a few advanced type features on top of recursive type aliases:
- Generics
- Infer
- Conditional Types
- keyof operator
The first part of the conditional type checks whether the path is a key of type T, if it is, then the type is set to be the key value: T[Path]
.
1 | Path extends keyof T ? T[Path] |
Then, infer
operator is used in the second part of the conditional type check to extract K
and R
out. Here, a pattern ${...}.${...}
is used to match the string with .
exists.
1 | Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropertyType<T[K], R> |
When all conditions match, we make a recursive call to the next level property PropertyType<T[K], R>
. Otherwise, it means the Path doesn’t exist, and a never
type will be returned.
Summary
In this article, we examine a few recursive type aliases. Recursive type alias can represent a data structure with a recursion nature, and we can also use it to traverse or manipulate complex data types.
I hope this article can be useful to you. Happy programming!
- Title: TypeScript Recursive Type Aliases Explained
- Author: Sunny Sun
- Created at : 2023-05-01 00:00:00
- Updated at : 2024-07-07 14:16:22
- Link: http://coffeethinkcode.com/2023/05/01/typescript-recursitve-type-aliases-explained/
- License: This work is licensed under CC BY-NC-SA 4.0.