题目描述 🎯
实现一个通用的Readonly2<T, K>,它带有两种类型的参数T和K。
K指定应该设置为Readonly2的T的属性集。如果未提供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
解题思路 💡
- 需要处理两个参数:
T和可选的K。 - 如果没有提供
K,则所有属性都应该是只读的。 - 如果提供了
K,则只有K中指定的属性是只读的。 - 使用交叉类型来合并只读和非只读的属性。
代码实现 ⌨️
// 实现方式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]
}
解题详解 🔍
- 第一部分:处理只读属性
{
readonly [P in K]: T[P]
}
- 遍历
K中的所有属性 - 添加
readonly修饰符 - 保持原有类型
T[P]
- 第二部分:处理非只读属性
{
[P in keyof T as P extends K ? never : P]: T[P]
}
- 遍历
T中的所有属性 - 使用
as过滤属性:- 如果属性在
K中,返回never(排除该属性)。 - 如果属性不在
K中,保留该属性。
- 如果属性在
- 保持原有类型
T[P]
- 使用交叉类型合并
... & ...
示例分析 🌟
- 示例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;
}
- 示例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;
}