类型体操之实现 Pick 和 Omit

简介: 之前深信服前端一面的时候,面试官问我会 TypeScript 吗?想到我之前说 Vue3 原理时顺便提了一口 Vue3 是基于 TypeScript 写的,我既然懂 Vue3 原理,那说不会一点 TypeScript 那实在是有点说不过去

之前深信服前端一面的时候,面试官问我会 TypeScript 吗?想到我之前说 Vue3 原理时顺便提了一口 Vue3 是基于 TypeScript 写的,我既然懂 Vue3 原理,那说不会一点 TypeScript 那实在是有点说不过去

结果就是面试官直接让我实现一个 Pick,直接戳穿了一个只用过 interfacetype 和泛型的 ts 小白,我虽然背过很多八股文,但很明显没背过 ts 的类型实现,而且只用过一两次 Pick,所以今天就借这篇文章写一写 PickOmit 的实现

为什么还要写 Omit,因为面试官说我不会写 Pick 可以写一个 Omit,但我两个都不会 /(ㄒoㄒ)/~~

202212012137235.png

type-challenges

如果你已经了解过关于 type-challenges 和其对应测试方法,可以跳过本章节,直接阅读Pick 章节部分

想要去试试水 ts 类型体操可以去 type-challenges/type-challenges 这个库,里面有很多奇奇怪怪的类型,不常见的那种

本项目意在于让你更好的了解 TS 的类型系统,编写你自己的类型工具,或者只是单纯的享受挑战的乐趣!我们同时希望可以建立一个社区,在这里你可以提出你在实际环境中遇到的问题,或者帮助他人解答疑惑 - 这些问题也可能被选中成为题库的一部分!

那么问题来了,PickOmit 在不在里面呢?

,而且是第 3 道和第 4 道题目,悔恨没有面试前看过这个库

在线测试

当我们选择题目之后,会有一个在线测试的链接

IMG

202212152201034.png

测试的目的是让框住部分的异常提示消失

IMG

Playground 的运行是不会有任何结果的,我写这个挑战的时候曾经一度以为是运行得到是否正确,但我一直没有运行成功

由于它里面有提供 Expect 等类型,我仍然怀疑是可以运行得到结果的,留个悬念,等我弄清楚了再来更新

Pick

题目描述

实现 TS 内置的 Pick<T, K>,但不可以使用它。

从类型 T 中选择出属性 K,构造成一个新的类型

例如:

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

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}

测试用例

/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Expected1, MyPick<Todo, 'title'>>>,
  Expect<Equal<Expected2, MyPick<Todo, 'title' | 'completed'>>>,
  // @ts-expect-error
  MyPick<Todo, 'title' | 'completed' | 'invalid'>,
]

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

interface Expected1 {
  title: string
}

interface Expected2 {
  title: string
  completed: boolean
}

题目模板

/* _____________ 你的代码 _____________ */

type MyPick<T, K> = any

解答

Pick 就是挑选的意思,描述里面就是挑了 titlecompleted 两个属性,那么在 ts 的层面需要如何实现呢?

首先你得保证 titlecompletedTodo 里面的属性,不是的话还挑什么呢?

对于这部分可以使用 keyof,其实 keyofJS 中也有个很像的 API,就是 Object.keys()Object.keys 的作用就是返回对象的 key(键) 列表,那么你应该知道 keyof 是干什么的了,就是用于获取某种类型的所有键,其返回类型是联合类型,它是 ts 的一个操作符

因此第一步实现如下,表示 KT 类型中的键

type MyPick<T, K extends keyof T> = any

回到描述里面的 TodoPreview,其实它的类型如下

interface TodoPreview {
  title: string
  completed: boolean
}

TodoPreview 其实就是 MyPick 返回的,换言之

type MyPick<Todo, 'title' | 'completed'> = {
  title: string
  completed: boolean
}

因此实现的思路就是需要在 MyPick 实现中补充一个泛型表示原 Todo 的键和对应类型,即如下

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

这里补充一下 ts 泛型的概念,ts 的泛型可以用来表示未确定的类型,同时在泛型之上添加约束,比如 K extends keyof T 即表示 T 所有键里面的任意一个

[P in K] 里面的 in 就不是这个意思了,inJS 的原生运算符,如果指定属性在指定对象中,则 in 运算符返回 true,而对于 ts 来说,这种语法 [P in K] 可以用来遍历联合类型里的类型(注意,遍历每一个),这也就是为什么不用 [P extends K],作用不一样哈

T[P] 的意思就比较好理解了,interface 具象化为对象,T[P] 不就是表示 T 中属性为 P 的类型值吗?即 Todo['title']: string

至此 Pick 实现,关键点为 keyofextendsin,泛型

相关文章
|
前端开发 JavaScript
Ts中逆变和协变—例子生动看完即懂
Ts中逆变和协变—例子生动看完即懂
181 0
|
JavaScript
学习TypeScrip11(类型推论|类型别名)
如果你声明变量没有定义类型也没有赋值这时候TS会推断成any类型可以进行任何操作
68 0
学习TypeScrip11(类型推论|类型别名)
|
前端开发 JavaScript 测试技术
类型体操之实现 type-challenges 中的 built-in 的所有类型
#built-in 是 type-challenges 库中的一个 tag,里面一共包括了 7 个类型,其中前两个就是之前介绍过的 类型体操之实现 Pick 和 Omit 中的 Pick 和 Omit
|
移动开发 缓存 HTML5
是时候学习/推广一波可选链(Optional chaining)和空值合并(Nullish coalescing )了
最近工作中发现团队有些同学不太了解 Optional chaining 和 Nullish coalescing 两个新的操作符,正好推广一波。
是时候学习/推广一波可选链(Optional chaining)和空值合并(Nullish coalescing )了
2019ICPC上海K-Color Graph(二分图 状压枚举)
2019ICPC上海K-Color Graph(二分图 状压枚举)
62 0
|
前端开发 JavaScript 容器
LPL Ban/Pick 选人阶段的遮罩效果是如何实现的?
LPL Ban/Pick 选人阶段的遮罩效果是如何实现的?
150 0
LPL Ban/Pick 选人阶段的遮罩效果是如何实现的?
|
存储 算法 API
Task03 彩色空间互转
Task03 彩色空间互转
90 0
Task03 彩色空间互转
|
JavaScript
手摸手一起学习Typescript第四天 - 类型推论,联合类型 和 类型断言
手摸手一起学习Typescript第四天 - 类型推论,联合类型 和 类型断言
|
JavaScript
手摸手一起学习Typescript第七天 - 类型别名 和 交叉类型
手摸手一起学习Typescript第七天 - 类型别名 和 交叉类型
|
JSON 开发工具 iOS开发
Objective-C使用位运算设计可复选的枚举
Objective-C使用位运算设计可复选的枚举
109 0
Objective-C使用位运算设计可复选的枚举