题目描述
实现一个泛型MyReadonly2,它带有两种类型的参数T和K。
类型 K 指定 T 中要被设置为只读 (readonly) 的属性。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly一样。
例如
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}
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 { Alike, Expect } 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 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
// ============= 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 T为K提供默认值,当未提供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中的键设置为只读
最后通过交叉类型&将两部分类型组合在一起