乐趣区

关于前端:TypeScript类型体操姿势合集easy题解

TypeScript 类型体操,核心思想是 通过类型生成新的类型!

记录一下 type-challenges 的简略题题解,以及解题思路。

仓库地址:https://github.com/type-chall…

博文地址:https://github.com/FrankKai/F…

4 – 实现 Pick

keyof / extends / in

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

解题思路

其中的 <T, K extends keyof T>:K 是公共类型,意思是 K 类型是 keyof T 后果的子集。这样做能保障 Pick 的第二个参数的类型,不在第一个参数的类型,会提醒报错

比方,上面代码传了一个在 Todo 类型上不存在的 ’invalid’,ts 会提醒报错”Type ‘”invalid”‘ is not assignable to type ‘keyof Todo’.“

// @ts-expect-error
type newType = MyPick<Todo, 'title' | 'completed' | 'invalid'>

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

https://github.com/type-chall…

7 – 实现 Readonly

readonly

type MyReadonly<T> = {readonly [P in keyof T] : T[P] }

解题思路

在属性名前减少 readonly,即可实现只读。

key 局部:利用 keyof T 失去 T 的键汇合,再利用 P in 做遍历,并为每个属性名减少 readonly 标识。

value 局部:T[P],复用原先的类型即可。

https://github.com/type-chall…

11 – 元组转换为对象

extends / readonly / in / T[number]

type TupleToObject<T extends readonly any[]> = { [P in T[number]: P }

解题思路

重点是“如何遍历元祖中的每一项?”,能够通过 number 索引,也就是 T[number] 获取到每一项。

为什么要 T extends readonly any[],这是因为要传入的是一个任意类型的数组,而 T[] 这种事不非法的,所以须要应用 extends 去结构一个新的类型。

https://github.com/type-chall…

14 – 第一个元素

extends / never / T[0]

type First<T extends any[]> = T extends [] ? never : T[0];

解析思路

第一项通过 T[0] 即可示意。

有一个用例须要留神:空数组时,第一项示意为 never。

重点是 如何判断空数组?有 2 种方法:T extends []T["length"] extends 0

https://github.com/type-chall…

18 – 获取元组长度

extends / readonly / T['length']

type Length<T extends readonly any[]> = T['length']

解题思路

能够通过 T[‘length’] 返回长度。

然而前提是结构一个新的类型,T extends any[]

因为元组类型是只读的,所以须要 T extends readonly any[]

与 11. 元组转对象思路统一

https://github.com/type-chall…

43 – Exclude

extends / never

type MyExclude<T, U> = T extends U ? never : T

解题思路

重点是如何移除 U,T 中的某一项如果在 U 中 (T extends U),返回 never 移除 U,否则返回 T。

https://github.com/type-chall…

189 – Awaited #11747

infer / 递归 / Promise<T>

type MyAwaited<T> = T extends Promise<infer P> 
  ? P extends Promise<any>
    ? MyAwaited<P>
    : P
  : never;

解题思路

infer + 递归 + Promise<T>

对于一般的类型,例如 type X = Promise<string>,用 T extends Promise<infer P> 即可判断出。
然而对于嵌套类型,例如 type Z = Promise<Promise<string | number>>,须要再判断一下 P 是否为 Promise 类型,若是的话,递归判断。

https://github.com/type-chall…

268 – If

extends boolean / extends true

if<C extends boolean, T, F> = C extends true ? T : F

解题思路

如何束缚泛型为 boolean 类型?C extends boolean。

如何判断以后泛型为 true?C extends true。

https://github.com/type-chall…

533 – Concat

...T 解构

type Contact<T extends any[], U extends any[]> = [...T, ...U]

解题思路

数组类型入参:T extends any[]

泛型解构(外围:通过 … 解构泛型变量):[…T, …U]

https://github.com/type-chall…

898 – Includes

infer / ...T 解构 / 递归

type MyEqual<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false

type Includes<T extends readonly any[], U> = T extends [infer P, ...infer Rest] 
  ? MyEqual<P, U> extends true 
    ? true
    : Includes<Rest, U>
  : false;

解题思路

解构 +infer+ 递归

判断形式:递归取 T 中第一项与 U 进行判断是否相等。

须要实现一个 Equal 函数,能够应用官网探讨中的 https://github.com/microsoft/…

https://github.com/type-chall…

3057 – Push

...T 解构

type Push<T extends any[], U> = [...T, U]

解题思路

束缚数组类型变量:T extends any[]

类型变量解构: …T

https://github.com/type-chall…

3060 – Unshift

...T 解构

type Unshift<T extends any[], U> = [U, ...T]

解题思路

束缚数组类型变量:T extends any[]

类型变量解构: …T

https://github.com/type-chall…

3312 – Parameters

infer

type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => unknown ? P : never;

解题思路

应用 infer 示意待推断的类型变量。

因为 …args 自身曾经是元组类型,因而 infer P 最终推导出的,也是元组类型。

https://github.com/type-chall…

退出移动版