对象属性只读(递归)
About 1 minTC-Medium
对象属性只读(递归)
题目
实现一个泛型 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`
解答
Tips
本题通过测试样例并不代表解答完全正确。本题将提供一个基本的解答用于启发思路,和一个在Vue.js源代码中的实现以供参考。
type DeepReadonly<T> = {
readonly [k in keyof T]:
T[k] extends Record<any, any> // 如果是对象
? T[k] extends Function // 如果是函数(注意,这里没考虑WeakMap等类型)
? T[k] // 不处理
: DeepReadonly<T[k]> // 递归地设为只读
: T[k] // 视为基本类型
}
Vue.js源代码中的实现:
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>
注意到,对于某些内置的类型,设置为只读后,并不返回原类型。当这些数据结构被设置为只读后,应移除产生修改的方法,例如set
、delete
等。