Recursive types in TypeScript
A type that refers to itself, which is how JSON and trees are typed.
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };
interface TreeNode<T> {
value: T;
children: TreeNode<T>[];
}
type Paths<T> = T extends object
? { [K in keyof T & string]: K | `${K}.${Paths<T[K]>}` }[keyof T & string]
: never;
type Config = { server: { port: number; host: string }; debug: boolean };
type ConfigPath = Paths<Config>;
const document: Json = { runs: [{ lang: "ts", tpm: 98 }], ok: true };
const tree: TreeNode<string> = {
value: "root",
children: [{ value: "leaf", children: [] }],
};
const path: ConfigPath = "server.port";
console.log(typeof document, tree.children.length, path);
How it works
- The recursion must go through an object, array or union.
- A depth limit keeps the compiler from giving up.
- This is the honest type for anything JSON-shaped.
Keywords and builtins used here
ConfigPathJsonTTreeNodebooleanconstdocumentextendsinterfacenumberstringtruetype
The run, in numbers
- Lines
- 27
- Characters to type
- 635
- Tokens
- 200
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 109 seconds.
Step 2 of 2 in Strings & recursion, step 8 of 12 in Type-level programming.