typescript 类型体操 之 9-medium-deep-readonly

416 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,在上一篇文章中,我们完成了中等的前三题,今天来做中等的第四题 9-medium-deep-readonly

下面这个是类型体操github仓库:

type-challenges/type-challenges: Collection of TypeScript type challenges with online judge (github.com)

9-medium-deep-readonly

今天的题目需要我们实现另一个进阶版本的 Readonly

image.png

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

image.png

代码中我们分为两种情况,一种是属性为对象的,另一种是属性不为对象的,分情况去做出对应的判断。

实现 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 就全部通过了。

知识点

关于上述提到了部分的知识点:

  1. 映射类型
  2. 条件类型
  3. 递归

关于映射类型和条件类型,之前都有详细的文章介绍,TS 的递归和 JS 语法是完全一致的。

总结

今天我们做完了中等的第四题,本题的解法比较多,本文只是列举了其中一种,具体的其他的方法可以去参考类型体操仓库里面的解答。