Some of the prior challenges are solvable without recursion, but this one absolutely requires it. If that scares you, don't worry, it's not so bad. Recursion is more-or-less the only mechanism we have for iteration in TypeScript, so we'll be using it a lot in the future.
🎥 Video Explanation
🔢 Code
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
type A1 = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
};
type B1 = {
readonly a: () => 22
readonly b: string
readonly c: {
readonly d: boolean
readonly e: {
readonly g: {
readonly h: {
readonly i: true
readonly j: 'string'
}
readonly k: 'hello'
}
readonly l: readonly [
'hi',
{
readonly m: readonly ['hey']
},
]
}
}
};
type C1 = Expect<Equal<DeepReadonly<A1>, B1>>;
// ============= Your Code Here =============
type DeepReadonly<T> =
keyof T extends never
? T
: {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
type DeepReadonly<T> = {
readonly [P in keyof T]:
keyof T[P] extends never
? T[P]
: DeepReadonly<T[P]>
};
type DeepReadonly<T> =
T extends object & { call?(): never } // exclude functions from `object` type, equivalent to `T extends Function`
? {
readonly [P in keyof T]: DeepReadonly<T[P]>
}
: T;
/*
1. You can assign anything to `{}`, *except* for `null` and `undefined`.
2. You can assign anything that's *not a primitive* to `object`.
- Primitives are strings, booleans, numbers, bigints, symbols, `null` and `undefined`
3. You can only assign *objects* to `Record<string, unknown>`
| Value is assignable to | {} | object | Record<string, unknown> |
| ---------------------- | --- | ------ | ----------------------- |
| null | No | No | No |
| undefined | No | No | No |
| "string" | Yes | No | No |
| 42 | Yes | No | No |
| 42n | Yes | No | No |
| Symbol() | Yes | No | No |
| true | Yes | No | No |
| () => {} | Yes | Yes | No |
| [1, 2] | Yes | Yes | No |
| [] | Yes | Yes | No |
| {foo: "bar"} | Yes | Yes | Yes |
| {} | Yes | Yes | Yes |
*/
9 - Deep Readonly
Some of the prior challenges are solvable without recursion, but this one absolutely requires it. If that scares you, don't worry, it's not so bad. Recursion is more-or-less the only mechanism we have for iteration in TypeScript, so we'll be using it a lot in the future.
🎥 Video Explanation
🔢 Code
âž• More Solutions
For more video solutions to other challenges: see the umbrella list! https://github.com/type-challenges/type-challenges/issues/21338