Deep Readonly
问题描述
实现一个泛型 DeepReadonly<T>,它将对象的每个参数及其子对象递归地设为只读。
您可以假设在此挑战中我们仅处理对象。不考虑数组、函数、类等。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。
例如
type X = {
x: {
a: 1
b: 'hi'
}
y: 'hey'
}
type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
}
type Todo = DeepReadonly<X> // should be same as `Expected`
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
type cases = [
Expect<Equal<DeepReadonly<X1>, Expected1>>,
Expect<Equal<DeepReadonly<X2>, Expected2>>,
Expect<Equal<DeepReadonly<X3>, Expected3>>,
Expect<Equal<DeepReadonly<X4>, Expected4>>
]
type X1 = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
}
]
}
}
}
type X2 = { a: string } | { b: number }
type X3 = { a: () => 22 }
type X4 = { a: Map<string, number> }
type Expected1 = {
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 Expected2 = { readonly a: string } | { readonly b: number }
type Expected3 = { readonly a: () => 22 }
type Expected4 = { readonly a: ReadonlyMap<string, number> }
// ============= Your Code Here =============
// 答案1
type DeepReadonly<T extends {}> = {
readonly [K in keyof T]: T[K] extends {}
? T[K] extends () => {}
? T[K]
: DeepReadonly<T[K]>
: T[K]
}
// 答案2
type DeepReadonly<T> = {
readonly [key in keyof T]: keyof T[key] extends never
? T[key]
: DeepReadonly<T[key]>
}
// 答案3
type DeepReadonly<T> = T extends Function
? T
: T extends object | unknown[]
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T
// 答案4
type Primitive = string | number | boolean | bigint | symbol | undefined | null
type Builtin = Primitive | Function | Date | Error | RegExp
type DeepReadonly<T> = T extends Builtin
? T
: T extends Map<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends ReadonlyMap<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends WeakMap<infer K, infer V>
? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends Set<infer U>
? ReadonlySet<DeepReadonly<U>>
: T extends ReadonlySet<infer U>
? ReadonlySet<DeepReadonly<U>>
: T extends WeakSet<infer U>
? WeakSet<DeepReadonly<U>>
: T extends Promise<infer U>
? Promise<DeepReadonly<U>>
: T extends {}
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: Readonly<T>
这题的思路首先是判断当前是否是对象的最后一层,不是则继续递归,一直判断到最后一层为止。
这里的思路很简单,首先将所有的键名变为只读属性 readonly [K in keyof T],其次判断键值是否继承自 {} 但是这里会判断不准确,()=>22 的也会判断为 true,所以需要继续判断一层 T[K] extends () => {},然而,答案1,2,3 只能符合某些类型,范围约束不明确,并不能将所有的类型都判断完整,这里的测试用例4就不能通过,所以最完整的判断应该是答案4,这里的答案将所有基本类型和复杂类型都枚举了出来进行判断。