ts类型挑战【十】

138 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

题目十三:omit

// template.ts
type MyOmit<T, K> = any
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Expected1, MyOmit<Todo, 'description'>>>,
  Expect<Equal<Expected2, MyOmit<Todo, 'description' | 'completed'>>>
]

// @ts-expect-error
type error = MyOmit<Todo, 'description' | 'invalid'>

interface Todo {
  title: string
  description: string
  completed: boolean
}

interface Expected1 {
  title: string
  completed: boolean
}

interface Expected2 {
  title: string
}

题目要求我们实现 ts 的内置类型 Omit,可参考TypeScript官方文档

官网解释:通过从类型中选取所有属性,然后移除键(字符串文字或字符串文字的并集)来构造类型。

也就是:参数1是一个对象,参数2是字符串或字符串的并集Omit 的作用就是将参数1中 key 为参数2的项删掉并返回

  • 示例1
interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
type TodoPreview = Omit<Todo, "description">; 

type TodoPreview的值:

type TodoPreview = {
    title: string;
    completed: boolean;
    createdAt: number;
}

相当于从接口 Todo 中删除了 keydescription 的这一项

  • 示例2
type TodoInfo = Omit<Todo, "completed" | "createdAt">;

type TodoInfo的值:

type TodoInfo = {
    title: string;
    description: string;
}

相当于从接口 Todo 中删除了 keycompletedcreatedAt 的这两项

代码实现

  • 原始代码
type MyOmit<T, K> = any
  • KT 中的键

可以让 K 继承自 Tkeys

type MyOmit<T, K extends keyof T> = any
  • object 的遍历

遍历的内容:Tkeyskeyof T),值就是原先 T 中的值(T[P]

type MyOmit<T, K extends keyof T> = {
  [P in keyof T]: T[P]
}

像上述代码这样的话,会遍历整个 T,没有起到过滤掉 K 的效果

  • 过滤 K

使用 as 转换,判断 P 是否存在于 K,如果存在则 as never(会跳过当前循环项),否则 as P

type MyOmit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P]
}

ts 内置 Omit写法

自己动手完成了一致,我们再来看一下官方的写法

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
  • K extends keyof any

通过 K extends keyof any,我们可以知道这里其实是不在乎 K 继承自什么的(K中的内容可以不存在于 T 中)

  • Exclude

我们之前实现过 Exclude<T, K>,它的作用就是去除 T 中的 K

type Exclude<T, K> = T extends K ? never : T;
  • Pick

Pick 相当于遍历的作用

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}
  • Pick<T, Exclude<keyof T, K>>

整体而言,相当于:

  1. 先把 T 中的 K 都给去除掉(假设去除后的内容为 U
  2. 再对 U 进行遍历