实现Readonly2

71 阅读2分钟

题目描述 🎯

实现一个通用的Readonly2<T, K>,它带有两种类型的参数TK

K指定应该设置为Readonly2T的属性集。如果未提供K,则应使所有的属性都变为只读,就想普通的Readonly<T>一样。

题目分析 📝

interface Todo {
  title: stirng
  description: string
  completed: boolean
}

const todo: MyReadonly2<Todo, 'title' | 'description'> = {
  title: "Hey",
  description: 'foobar',
  completed: false
}

todo.title = "Hello" // Error: Cannot assign to 'title' because it is a read-only property.
todo.description = "barFoo" // Error: Cannot assign to 'description' because it is a read-only property.
todo.completed = true // OK

解题思路 💡

  1. 需要处理两个参数:T和可选的K
  2. 如果没有提供K,则所有属性都应该是只读的。
  3. 如果提供了K,则只有K中指定的属性是只读的。
  4. 使用交叉类型来合并只读和非只读的属性。

代码实现 ⌨️

// 实现方式1:
type Readonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in keyof T as P extends K ? never : P]: T[P]
}

// 实现方式2:
type Readonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in Exclude<keyof T, K>]: T[P]
}

解题详解 🔍

  1. 第一部分:处理只读属性
{
  readonly [P in K]: T[P]
}
  • 遍历K中的所有属性
  • 添加readonly修饰符
  • 保持原有类型T[P]
  1. 第二部分:处理非只读属性
{
  [P in keyof T as P extends K ? never : P]: T[P]
}
  • 遍历T中的所有属性
  • 使用as过滤属性:
    • 如果属性在K中,返回never(排除该属性)。
    • 如果属性不在K中,保留该属性。
  • 保持原有类型T[P]
  1. 使用交叉类型合并
... & ...

示例分析 🌟

  1. 示例1:部分属性只读。
type Readonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in keyof T as P extends K ? never : P]: T[P]
}

interface Todo {
  title: string; // 设置为只读
  description: string; // 设置为只读
  completed: boolean; // 保持可写
}

type R1 = Readonly2<Todo, 'title' | 'description'>

// 第一部分:处理 K 中的属性(title 和 description)
{
  readonly title: string;
  readonly description: string;
}

// 第二部分:处理剩余属性
{
  // title extends 'title' | 'description' ? never : 'title' => never
  // description extends 'title' | 'description' ? never : 'description' => never
  // completed extends 'title' | 'description' ? never : 'completed' => 'completed'
  completed: boolean;
}

// 最终结果
{
  readonly title: string;
  readonly description: string;
  completed: boolean;
}
  1. 示例2:全部属性只读
type Readonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in keyof T as P extends K ? never : P]: T[P]
}


interface Todo {
  title: string
  description: string
  completed: boolean
}

type R2 = Readonly2<Todo> // K 默认为 keyof T

// 第一部分:处理所有属性(因为 K = keyof T)
{
  readonly title: string;
  readonly description: string;
  readonly completed: boolean;
}

// 第二部分:
{
  // 所有属性都在 K 中,所以都返回 never
  // 没有任何属性
}

// 最终结果
{
  readonly title: string;
  readonly description: string;
  readonly completed: boolean;
}