Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DeepReadonly type fails to compile on typescript 3.1.1+ #29566

Closed
masterkidan opened this issue Jan 24, 2019 · 5 comments
Closed

DeepReadonly type fails to compile on typescript 3.1.1+ #29566

masterkidan opened this issue Jan 24, 2019 · 5 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@masterkidan
Copy link

masterkidan commented Jan 24, 2019

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

@masterkidan
Copy link
Author

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

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Feb 5, 2019
@Dean177
Copy link

Dean177 commented Mar 4, 2019

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

@masterkidan
Copy link
Author

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

@dragomirtitian
Copy link
Contributor

dragomirtitian commented Mar 8, 2019

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]> }

@aleclarson
Copy link

aleclarson commented Mar 8, 2019

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

6 participants