Skip to content

DeepReadonly type fails to compile on typescript 3.1.1+ #29566

Closed

Description

TypeScript Version: 3.1.1+

Search Terms: DeepReadonly

Code

export type Primitive = string | number | boolean | undefined | null | Function | symbol;
export type DeepReadonly<T> =
  T extends [infer A]                                                       ? DeepReadonlyObject<[A]> :
  T extends [infer A, infer B]                                              ? DeepReadonlyObject<[A, B]> :
  T extends [infer A, infer B, infer C]                                     ? DeepReadonlyObject<[A, B, C]> :
  T extends [infer A, infer B, infer C, infer D]                            ? DeepReadonlyObject<[A, B, C, D]> :
  T extends [infer A, infer B, infer C, infer D, infer E]                   ? DeepReadonlyObject<[A, B, C, D, E]> :
  T extends [infer A, infer B, infer C, infer D, infer E, infer F]          ? DeepReadonlyObject<[A, B, C, D, E, F]> :
  T extends [infer A, infer B, infer C, infer D, infer E, infer F, infer G] ? DeepReadonlyObject<[A, B, C, D, E, F, G]> :
  T extends Map<infer U, infer V> ? ReadonlyMap<DeepReadonlyObject<U>, DeepReadonlyObject<V>> :
  T extends Set<infer U> ? ReadonlySet<DeepReadonlyObject<U>> :
  T extends Promise<infer U> ? Promise<DeepReadonlyObject<U>> :
  T extends Primitive ? T :
  T extends (infer A)[]           ? DeepReadonlyArray<A> :
  DeepReadonlyObject<T>;

interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }

Expected behavior:
This used to work as of typescript 3.0.1

Actual behavior:
In typescript 3.1.1 a new error is thrown saying that the type is cyclical
In typescript 3.2+ the above error, instead ends up casting the type as any
Playground Link:
Here's a gist with tests for the type
https://gist.github.com/masterkidan/7322752f569b1bba53e0426266768623

Playground link

Related Issues:
#27421
#13923
#26236

Activity

  1. masterkidan commented on Jan 24, 2019

    @masterkidan
    Author

    I understand this may be by design, but is there a way to achieve the same with newer versions of typescript?

  2. Dean177 commented on Mar 4, 2019

    @Dean177

    Mukundan Kidambi (@masterkidan) looks like this is due to the tuple types, without those your code works as expected
    Playground link

  3. masterkidan commented on Mar 8, 2019

    @masterkidan
    Author

    Dean Merchant (@Dean177) : Thanks!, is there a way to make this work without removing support for Tuples?

  4. dragomirtitian commented on Mar 8, 2019

    @dragomirtitian
    Contributor

    Actually I think you are better off wating for 3.4 and Improved support for read-only arrays and tuples (#29435). With this new feature mapped types will apply the readonly modifier to tuples and arrays and you will not need any special handling for them. This will work as expected (as far as I can tell) in 3.4 (you can try it out with npm install typescript@next:

    export type DeepReadonly<T> =
      T extends Map<infer U, infer V> ? ReadonlyMap<DeepReadonlyObject<U>, DeepReadonlyObject<V>> :
      T extends Set<infer U> ? ReadonlySet<DeepReadonlyObject<U>> :
      T extends Promise<infer U> ? Promise<DeepReadonlyObject<U>> :
      T extends Function ? T : DeepReadonlyObject<T> 
    
    type DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }
  5. aleclarson commented on Mar 8, 2019

    @aleclarson

    Immer has a "deep readonly" type called Immutable (see here) that works fine in 3.1+

    (No support for Map, Set, or Promise like the OP, but you can add that yourself rather easily)

  6. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions