# 前置知识
今日 【middle - 8.readonly2】
在这之前需要掌握一点Ts基础知识,可以参考学习记录TypeScript学习记录-[数据类型]、TypeScript学习记录-[类和接口]、TypeScript学习记录-[枚举和泛型]、TypeScript学习记录-[类型别名]
# 二、题目分析
# readonly2
实现一个通用MyReadonly2<T, K>,它带有两种类型的参数T和K。K指定应设置为Readonly的T的属性集。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly
type MyReadonly2<T, K> = any
/* _____________ 测试用例 _____________ */
import type { Alike, Expect } from '@type-challenges/utils'
type cases = [
Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>,
]
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
}
- 首先可以确定的是返回类型是
object
(索引类型index Type), 且其中属于泛型K
的属性都是readonly
属性,即type MyReadonly2<T, keys extends keyof T> = { readonly [key in keys]: T[key] }
,其中keys extends keyof T
对联合类型keys
进行类型约束,确保后续使用in
遍历keys
时的每一个类型key
均在泛型T
的类型中 - 接下来要取包含在泛型
T
但不包含在泛型k(keys)
中的属性,可以这样写[key in keyof T as key extends keys ? never : key]: T[key]
,这里的重映射运算符as
将在遍历运算中的每一项类型key
使用extends ? :
进行条件判断,符合前述条件的类型将被返回,而属于泛型k(keys)
的属性将被丢弃(never
代表不可达) - 经过上述分析我们将得到
type MyReadonly2<T, keys extends keyof T> = { readonly [key in keys]: T[key] } & { [key in keyof T as key extends keys ? never : key]: T[key] }
,这里通过交叉类型(Intersection
)运算对类型做合并(两者都属于obj
类型,同一类型可以合并,不同的类型无法合并),这样经过MyReadonly2
运算就得到了一个K
指定应设置为Readonly
的T
的属性集 - 值得注意的是
<T, Keys extends keyof T> = { [key in keyof T as key extends Keys ? never : key]: T[key] }
是Omit<T, K>
泛型的实现,前述内容可以简化为type MyReadonly2<T, keys extends keyof T> = { readonly [key in keys]: T[key] } & Omit<T, K>
- 为了实现
如果未提供K,则应使所有属性都变为只读,就像普通的Readonly<T>一样
这个条件,在keys extends keyof T
中,需要对泛型keys
设定原始类型,即keys = keyof T
,加上类型约束就得到了keys extends keyof T = keyof T
,从而得到了最终的结果type MyReadonly2<T, keys extends keyof T = keyof T> = { readonly [key in keys]: T[key] } & { [key in keyof T as key extends keys ? never : key]: T[key] }