[TypeScript] Type Challenges #8- Readonly 2

70 阅读1分钟

题目描述

实现一个泛型MyReadonly2,它带有两种类型的参数TK

类型 K 指定 T 中要被设置为只读 (readonly) 的属性。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly一样。

例如

interface Todo {
  titlestring
  descriptionstring
  completedboolean
}

const todoMyReadonly2<Todo'title' | 'description'> = {
  title"Hey",
  description"foobar",
  completedfalse,
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK

题解

// ============= Test Cases =============
import type { AlikeExpect } from './test-utils'

type cases = [
  Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
  Expect<Alike<MyReadonly2<Todo1'title' | 'description'>, Expected>>,
  Expect<Alike<MyReadonly2<Todo2'title' | 'description'>, Expected>>,
  Expect<Alike<MyReadonly2<Todo2'description' >, Expected>>,
]

// @ts-expect-error
type error = MyReadonly2<Todo1'title' | 'invalid'>

interface Todo1 {
  titlestring
  description?: string
  completedboolean
}

interface Todo2 {
  readonly titlestring
  description?: string
  completedboolean
}

interface Expected {
  readonly titlestring
  readonly description?: string
  completedboolean
}

// ============= Your Code Here =============
type MyReadonly2<T, K extends keyof T = keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P]
} & {
  readonly [P in K]: T[P]
}

使用K extends keyof T对类型参数K进行约束,确保K仅包含对象类型T的键,防止传入T中不存在的键

使用K = keyof TK提供默认值,当未提供K时,会将T的所有属性设为只读

[P in keyof T as P extends K ? never : P]: T[P]遍历T的键,通过as关键字重新映射。如果P属于K,将键映射为never,使其在最终类型中被排除;否则映射为原值

readonly [P in K]: T[P]K中的键设置为只读

最后通过交叉类型&将两部分类型组合在一起