Skip to content

13. DeepReadonly #19

@astak16

Description

@astak16

题目

题目链接:DeepReadonly

实现类型 DeepReadonly ,将对象的每个子对象也变成只读。

import type { Equal, Expect } from "@type-challenges/utils";

type cases = [Expect<Equal<DeepReadonly<X>, Expected>>];

type X = {
  a: () => 22;
  b: string;
  aa: {};
  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 aa: {};
  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 DeepReadonly<T> = T extends object & { call?: never }
  ? { readonly [key in keyof T]: DeepReadonly<T[key]> }
  : T;

知识点

{call?: never} 是用来排除函数的

方法二

type DeepReadonly<T> = {
  readonly [key in keyof T]: T[key] extends Function
    ? T[key]
    : T[key] extends object
    ? DeepReadonly<T[key]>
    : T[key];
};

知识点

通过 T[key] extends Function 判断是不是函数,不是函数的话在判断是不是 object

方法三

type DeepReadonly<T> = {
  readonly [key in keyof T]: keyof T[key] extends never
    ? T[key]
    : DeepReadonly<T[key]>;
};

// 等价于

type DeepReadonly<T> = {
  readonly [key in keyof T]: keyof T[key] extends object
    ? T[key]
    : DeepReadonly<T[key]>;
};

// 等价于

type DeepReadonly<T> = keyof T extends never
  ? T
  : { readonly [key in keyof T]: DeepReadonly<T[key]> };

// 等价于

type DeepReadonly<T> = keyof T extends object
  ? T
  : { readonly [key in keyof T]: DeepReadonly<T[key]> };

知识点

keyof T[key] extends never,如果 T[key] 为函数或者空对象, keyof T[key] 结果为 never

  • keyof () => voidnever
  • keyof {}never

同时 never extends objecttrue

这样就把函数和空对象排除掉了

方法四

type DeepReadonly<T> = T extends Record<string, unknown> | Array<unknown>
  ? { readonly [key in keyof T]: DeepReadonly<T[key]> }
  : T;

知识点

const a: Record<string, unknown> = () => 1; // 报错
const c: Record<string, any> = () => 1; // 不报错

所以 Record<string, unknown> 不能用 any 代替 unknown

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions