持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
前言
在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,在上一篇文章中,我们完成了中等的前三题,今天来做中等的第四题 9-medium-deep-readonly
下面这个是类型体操github仓库:
9-medium-deep-readonly
今天的题目需要我们实现另一个进阶版本的 Readonly
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<DeepReadonly<X>, Expected>>,
]
type X = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
}
type Expected = {
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']
},
]
}
}
}
TS 存在着内置的工具类型 Readonly 能够将所有的对象属性都转为只读,题目需要我们提供一个升级版本,将对象下的属性全部变为只读,再进阶一点,需要将对象中的数组元素或者函数也变为只读的。
利用JS进行对比学习
我们使用js来模拟一下题目需要的过程,传入一个对象并且这个对象的属性里面也存在着对象
通过题目的要求,我们能够想到这道题需要使用递归来完成,当对象的属性不是对象的时候,就给它加上 readonly,是一个对象的话,就将这个对象再次传入 deepReadonly 函数当中。
function deepReadonly(Obj) {
const NewObj = {};
for (let key in Obj) {
if (typeof Obj[key] == "object") {
NewObj["readonly " + key] = deepReadonly(Obj[key]);
} else {
NewObj["readonly " + key] = Obj[key];
}
}
return NewObj;
}
代码中我们分为两种情况,一种是属性为对象的,另一种是属性不为对象的,分情况去做出对应的判断。
实现 DeepReadonly
首先根据上面的 JS 分析,我们能够得到,我们需要去遍历对象的所有键值,这点使用映射类型就可以实现。
然后在遍历的时候,我们就能够用条件类型来实现类型判断,进行各种分叉,通过 JS 代码我们就能够判断出来,在属性为 string, number, boolean 或者 Function的时候,只需要加上 readonly 前缀,不然的话,就需要将当前项进入递归。
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends string | number | boolean | Function
? T[K]
: DeepReadonly<T[K]>
}
到这里所有的 测试 case 就全部通过了。
知识点
关于上述提到了部分的知识点:
- 映射类型
- 条件类型
- 递归
关于映射类型和条件类型,之前都有详细的文章介绍,TS 的递归和 JS 语法是完全一致的。
总结
今天我们做完了中等的第四题,本题的解法比较多,本文只是列举了其中一种,具体的其他的方法可以去参考类型体操仓库里面的解答。