3 - 实现 Omit
不使用 Omit 实现 TypeScript 的 Omit<T, K> 泛型。
Omit 会创建一个省略 K 中字段的 T 对象。
例如:
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false
}
Solution
Omit 用于从一个类型中剔除某些属性,并返回一个新的类型
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
MyOmit 接收两个泛型
T需要剔除属性的类型K需要剔除的属性名,并约束了属性名必须是 T 的属性名
keyof T 返回 T 中所有属性名组成的联合类型, 如
type User = {
name: string
age: number
}
type KeyOfUser = keyof User // 'name' | 'age'
在具体的实现中使用了 映射类型(Mapped Types), 可以将一个类型映射成另一个类型,语法如下:
type TypeName<T> = {
[P in keyof T]: T[P]
}
in 相当于循环后面的联合类型, P 作为子项。T[P] 返回类型 T 中属性名为 P 的对应的类型
再回到 MyOmit 的实现
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
P in keyof T as P extends K ? never : P 可以划分为 (P in keyof T) as (P extends K ? never : P)
P in keyof T: 遍历 T 中的每个属性键,P 是代表每个属性键的变量as P extends K ? never : P:as关键字后面的部分是对 类型P 的类型转换 。这是一个条件类型,如果 P 是 K 中的一部分,那么我们将其重命名为 never,否则我们保持其原来的名字 P
示例:
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false
}
keyof T 返回 'title' | 'description' | 'completed'
在 [P in keyof T] 中进行第一次循环 P 为 'title', 得到
'title' as ('title' extends 'description' | 'title' ? never : 'title')
由于 'title' extends 'description' | 'title' 为 true, 所以返回 never, 不进行处理
P 为 'description' 时同理;
当 P 为 completed 时:
'completed' as ('completed' extends 'description' | 'title' ? never : 'completed')
由于 'completed' extends 'description' | 'title' 为 false, 返回 'completed', 故返回类型 'completed': T['completed']