Recursive types — a type for any JSON value¶
Level: 301 · for anyone who has typed data read from a file
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The questions below are what the finished page has to answer.
One line: type Json = … | Json[] | { [key: string]: Json } names itself and covers JSON at any depth, yet a value typed by an interface with the same fields is rejected, because only type aliases get an implicit index signature.
Keywords: recursive type, implicit index signature
What the finished page will answer¶
- How can
type Jsonmention itself, and how deep a value does it accept? - Why is a value typed by
interface Userrejected asJsonwhen the same fields undertype Userare accepted? - Why are
new Date()and{ a: undefined }rejected, whenJSON.stringifyhandles both? - Which values does
Jsonaccept that JSON cannot hold — what doesJSON.stringify(NaN)produce? - When does tsc give up on a recursive type with TS2589, 'Type instantiation is excessively deep and possibly infinite'?
Examples it will need¶
-
recursive_types_json_tserror.ts— the errors for an interface value, a Date and an undefined field assigned to Json -
recursive_types_nan_ts.ts— JSON.stringify of values the Json type accepts, including NaN and Infinity, which come out as null -
recursive_types_depth_tserror.ts— the TS2589 error from a recursive type that counts too deep
See also¶
- JSON — what JSON.stringify drops, changes and refuses
- Type aliases and interfaces — where aliases and interfaces behave differently
- External data — what JSON.parse returns instead of Json: any
- Run-time validation — checking at run time that a value really is Json
- Rust: A generic recursive type ↗ — in Rust a Box must break the cycle; a type alias needs nothing