TypeScript型別體操姿勢合集-easy題解

趁你還年輕發表於2022-06-17

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...

相關文章