Type Challenges Notes
最近更新日期:2023/05/08
Easy
11 - Tuple to Object ➜
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
加上
as const
會將普通的 Array 直接轉成 Tuple。
- Tuple:
const tuple: readonly ["tesla", "model 3", "model X", "model Y"]
- Array:
const tuple: string[]
type TupleToObject<T extends readonly (string | number)[]> = {
[P in T[number]]: P
}
這裡的
T[number]
包含'tesla'|'model 3'|'model X'|'model Y'
。number
指的是 index(全部的數字),有取到的值(e.g.T[0]
:'tesla'
)就會存在,沒有取到的值(e.g.T[5]
:never
)就是不存在,全部做成 Union 就是T[number]
的結果。
14 - First of Array ➜
TheType<[3, 2, 1]>
直接寫在泛型
< >
裡面就會是 Tuple。
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]
T['length']
可以直接取到長度。
type First<T extends any[]> = T extends [infer R, ...any[]] ? R : never
infer R
必須使用在「條件類型的子句」,也就是extends
後面、?
前面的位置。
用來推斷此位置的型別(存成變數用在同一行的其他地方)。
189 - Awaited ➜
PromiseLike<any>
擁有
.then()
方法的物件(thenable),只要有.then()
函式就會將其當做一個 Promise 實例。
898 - Includes ➜
Equal<U, R>
推斷出兩個型別是否絕對相同。
Medium
3 - Omit ➜
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
將
K extends keyof T
寫在< >
裡面會限制傳入的參數。
Exclude<T, K>
需要兩個參數
T
和K
,把T
中滿足K
的剔除。
- Omit:從「物件」屬性內剔除一個或多個屬性
- Exclude:從「聯集型別」內剔除一個或多個
8 - Readonly 2 ➜
type MyReadonly2<T, K extends keyof T = keyof T> =
Readonly<Pick<T, K>> & Omit<T, K>
<T, K extends keyof T = keyof T>
的= keyof T
是第二參數的預設值,意即在傳入參數的時候允許只傳入第一個參數。
9 - Deep Readonly ➜
type DeepReadonly<T> = T extends Function
? T
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T
Function
跟(...args: any[]) => any
不一樣。Function
可能包含其他不清楚的東西(少用比較好)。
type DeepReadonly<T> = {
readonly [P in keyof T]: keyof T[P] extends never
? T[P]
: DeepReadonly<T[P]>
}
判斷
T[P]
的 keys 是不是不存在(沒有下一層)。
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends string | number | boolean | Function
? T[P]
: DeepReadonly<T[P]>
}
判斷
T[P]
是不是沒有 key 的型別(沒有下一層)。
12 - Chainable Options ➜
type Chainable<T = {}> = {
option: <K extends string, V>(
key: K extends keyof T ? never : K,
value: V
) => Chainable<Omit<T, K> & Record<K, V>>,
get: () => T
}
<K extends string, V>
是傳入參數型別的泛型,因為key
、value
有用到型別參數,所以會自行推斷型別,不需要再額外寫一次。.option
需要回傳 Chainable,才能再繼續接.option
。
15 - Last of Array ➜
type Last<T extends any[]> = [undefined, ...T][T['length']]
[T['length']]
會拿到[數量]
。
在原陣列的最前端加上一項任意型別,讓長度可以剛好取到最後一項。
20 - Promise.all ➜
declare function PromiseAll<T extends readonly any[]>(values: [...T])
declare function PromiseAll<T extends any[]>(values: readonly [...T])
寫在泛型裡的
readonly
是用來限制傳入的參數。
寫在( )
裡的readonly
是用來標明實際 function 內部不會動到傳入的值。 若泛型限制readonly
-> 可以傳入一般參數或readonly
的參數(因為在 function 內皆不會被改動),反過來若無限制則不可傳入readonly
的參數。
declare function PromiseAll<T extends any[]>(
values: readonly [...T]
): Promise<{
[P in keyof T]: T[P] extends infer A
? A extends Promise<infer R> ? R : A
: never
}>
假設
T[P]
是number | Promise<number>
,則 extends 的判斷T[P]
會代表(number | Promise<number>)
,並不會將|
的兩項分開處理。T[P] extends infer A
將T[P]
先存成變數(非用中括號取值)再做 extends 的判斷,則會將number
與Promise<number>
分開動作。
62 - Type Lookup ➜
type LookUp<U, T> = U['type'] extends T ? U : never
這樣是無效的!
Conditional Types 只有在具有T extends ...
形式(Naked Type)時才會有分配性,而在extends
後面複雜的表達式也不會觸發分配性。
106 - Trim Left ➜
type Space = ' ' | '\n' | '\t'
type TrimLeft<S extends string> =
S extends `${Space}${infer R}`
? TrimLeft<R>
: S
字串可以
${Space}${infer R}
像這樣組起來(類似陣列)。
110 - Capitalize ➜
type MyCapitalize<S extends string> =
S extends `${infer R}${infer Rest}`
? `${Uppercase<R>}${Rest}`
: ''
Uppercase<R>
可以直接轉大寫。
296 - Permutation ➜
[T] extends [never]
- Tuple
T[] extends [never]
- Array
要判斷是不是
never
無法直接判斷,需要以這種方式。
K extends K
當 Naked Type 遇到
extends
就會觸發分配性,不一定要是extends
自己,any
或其他也可以,主要是有extends
。
298 - Length of String ➜
type LengthOfString<S extends string, T extends string[] = []> =
S extends `${infer R}${infer Rest}`
? LengthOfString<Rest, [...T, R]>
: T['length']
當 S 只有一個字(例如:
'x'
),R
會取到x
,Rest
會取到''
,所以S extends ${infer R}${infer Rest}
的結果仍會是true
。
527 - Append to object ➜
type AppendToObject<T extends Object, U extends string, V extends any> = T & {
[K in U]: V
}
這樣寫的型別會變成
TypeA & {...}
,和併成一整個物件型別不一定相同(若有相同key
的情況下型別可能會不同)。 要寫成K in U
才會進行迭代。
type AppendToObject<T, U extends string, V> =
Omit<T & { [P in U]: V }, never>
type AppendToObject<T, U extends string, V> =
T & { [K in U]: V } extends infer A
? { [K in keyof A]: A[K] }
: never
Omit<T, never>
或利用extends infer A
可以將 & 的物件型別們合併在一起。
529 - Absolute ➜
type AAA = Absolute<9_999n> // "9999"
type Absolute<T extends number | string | bigint> = `${T}`
type AAA = Absolute<9_999n> // 9999n
type Absolute<T extends number | string | bigint> = T
bigint
:123n
(末尾有n
)在 TypeScript 中,透過
${}
將陣列轉換為字串時,編譯器會自動進行轉換,因此透過${T}
可以直接取得轉換後的9999
。
599 - Merge ➜
type Merge<F, S> = {
[K in keyof F | keyof S]: K extends keyof S
? S[K]
: K extends keyof F
? F[K]
: never
}
[K in keyof F | keyof S]
無法直接取S[K]
,需要透過K extends keyof S
。
612 - KebabCase ➜
Uncapitalize<T>
將
T
字串中的第一個字元轉換為小寫字母。
Capitalize<T>
將
T
字串中的第一個字元轉換為大寫字母。
Lowercase<T>
將
T
字串中的每個字元轉換為小寫字母。
645 - Diff ➜
type O = {
name: string
age: string
}
type O1 = {
name: string
age: string
gender: number
}
型別們:
typescriptkeyof O & keyof O1 // "name" | "age" keyof O | keyof O1 // "name" | "age" | "gender" keyof (O & O1) // "name" | "age" | "gender"
type Diff<O, O1> = {
[K in (keyof O | keyof O1) as Exclude<K, keyof O & keyof O1>]: (O & O1)[K]
}
type Diff<O, O1> = {
[K in keyof (O & O1) as K extends keyof (O | O1) ? never : K]: (O & O1)[K]
}
這裡的
as
是可以進一步對迭代到的K
進行操作。
key 為never
的話會直接去掉此項。
949 - AnyOf ➜
type AnyOf<T extends readonly any[]> = T extends [infer R, ...infer Rest]
? R extends 0 | '' | false | [] | { [x: string]: never } | undefined | null
? AnyOf<Rest>
: true
: false
檢查空物件不能直接寫
{}
,要用{ [x: string]: never }
或Record<string, never>
。
1097 - IsUnion ➜
type IsUnion<T, K = T> = [T] extends [never]
? false
: T extends K
? [K] extends [T] ? false : true
: never
在
T extends K
的這行就會觸發分配性了,只是通常要再下一行才能進行其他操作。[K] extends [T]
是將K
和T
包在 Tuple 裡,為了不要觸發分配性。
1367 - Remove Index Signature ➜
將
[key: any]: any
(Index Signature)去除。
type PropertyKey = string | number | symbol
type RemoveIndexSignature<T, P = PropertyKey> = {
[
K in keyof T as P extends K
? never
: K extends P
? K
: never
]: T[K]
}
K in keyof T as P extends K
透過前面的K in keyof T
拿到迭代的K
,即可列成條件P extends K
。 留下P extends K
不成立 &K extends P
成立的情況。
2257 - MinusOne ➜
type S = '12345' // '123'
type N = S extends `${infer R extends number}` ? R : never // 123
可以將型別
"123"
轉換成型別123
。
type NumberLiteral = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
type S = '12345' // "12345"
type RemoveLastChar = S extends `${infer Rest}${NumberLiteral}` // "1234"
? S extends `${Rest}${infer Last extends NumberLiteral}`
? Rest
: never
: never
透過此方式可以取得
Rest
:去除最後一項字元的字串及Last
:最後一項字元。
2757 - PartialByKeys ➜
Partial<T>
建構一個型別,其中
T
的所有屬性都被設置為 optional。
2759 - RequiredByKeys ➜
{
[P in keyof T as P extends K ? P : never]-?: T[P]
}
可以透過
-?
將 optional 改為 required。
2793 - Mutable ➜
type Mutable<T extends Record<string, any> | readonly any[]> = {
-readonly [K in keyof T]: T[K]
}
可以透過
-readonly
將 readonly 屬性去除。
2946 - ObjectEntries ➜
type ObjectEntries<T> = {
[K in keyof T]-?:
[K, T[K] extends (infer L | undefined)
? L
: T[K]
]
}[keyof T]
只有
{}
區塊的話會拿到:typescripttype AAA = { name: ["name", string] age: ["age", number] locations: ["locations", string[] | null] }
再透過
[keyof T]
取得各項 key 的值。
3188 - Tuple to Nested Object ➜
type TupleToNestedObject<T extends string[], U> =
T extends [infer R extends string, ...infer Rest extends string[]]
? { [K in R]: TupleToNestedObject<Rest, U> }
: U
使用
[K in R]
而不是[K in keyof R]
,因為R
不是物件型別。 除了用infer R extends string
做限制之外,在 key 的地方寫成[K in R & string]
也可以。
3196 - Flip Arguments ➜
T extends (...args: infer A) => any
Function 型別可以透過此方式拿到參數型別(將未知數量的參數展開後的型別參考訂為
A
)。 假設傳入的參數型別是(arg0: string, arg1: number, arg2: boolean) => void
。
則推斷完A
的型別為[arg0: string, arg1: number, arg2: boolean]
type FlipArguments<T extends (...args: any[]) => any> =
T extends (...args: infer A) => infer R ? (...args: Reverse<A>) => R : never
透過
...args
拿到參數陣列args
,而args
型別推斷是A
。
Function 的參數名稱args
需要一直寫著(實際在寫 Function 的時候本來就會寫)。
3243 - FlattenDepth ➜
type FlattenDepth<A extends any[], N extends number = 1, R extends any[] = []> =
R['length'] extends N
? A
: A extends [infer F, ...infer Rest]
? F extends any[]
? [...FlattenDepth<F, N, [...R, undefined]>, ...FlattenDepth<Rest, N, R>]
: [F, ...FlattenDepth<Rest, N, R>]
: A
利用
R
陣列的長度紀錄目前展開幾層,利用...
展開陣列,每次展開都將R
陣列多加一項。
3326 - BEM style string ➜
type BEM<
B extends string,
E extends string[],
M extends string[]
> = `${B}${
E extends [] ? '' : `__${E[number]}`
}${
M extends [] ? '' : `--${M[number]}`
}`
可以直接在
${}
裡面做判斷。
3376 - InorderTraversal ➜
Inorder traversal (中序遍歷) 會先拜訪左子節點,再拜訪父節點,最後拜訪右子節點。
type InorderTraversal<T extends TreeNode | null> =
T extends TreeNode
? [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
: []
type InorderTraversal<T extends TreeNode | null> =
T extends null
? []
: [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
T extends TreeNode
或是T extends null
會讓T
產生分配性。
需要判斷的話可以用 Tuple 包起來,例如:[T] extends [TreeNode]
。
NonNullable<T>
從型別
T
中刪除null
和undefined
。
4179 - Flip ➜
type Flip<T extends Record<string, any>> = {
[K in keyof T as `${T[K]}`]: K
}
可以直接寫
as
後面作為 key 名稱。
4182 - Fibonacci Sequence ➜
type Fibonacci<
T extends number,
CurrentIndex extends any[] = [undefined],
Prev extends any[] = [],
Current extends any[] = [undefined]
> = CurrentIndex['length'] extends T
? Current['length']
: Fibonacci<T, [...CurrentIndex, undefined], Current, [...Prev, ...Current]>
利用陣列
Current
的總長度計算 Fibonacci 數列的數字。
(目的是拿到總長度,所以塞什麼進陣列都不影響,因此放了undefined
)CurrentIndex
陣列的總長度是負責記錄是否已達到題目要求(T
)。
4260 - AllCombinations ➜
type AllCombinations<
S extends string,
P extends string = ''
> = S extends `${infer F}${infer Rest}`
? `${F}${AllCombinations<`${P}${Rest}`>}` | AllCombinations<Rest, `${P}${F}`>
: ''
4425 - Greater Than ➜
type GreaterThan<T extends number, U extends number, R extends any[] = []> =
T extends R['length']
? false
: U extends R['length']
? true
: GreaterThan<T, U, [...R, 1]>
讓
R['length']
從0
開始往上加。
若T extends R['length']
成立,則表示U
不可能大於T
。
若U extends R['length']
成立,則表示T
肯定大於T
。
4471 - Zip ➜
[T, U] extends [[infer L, ...infer RestT], [infer R, ...infer RestU]]
可以將多個判斷用 Tuple 包起來做
extends
。
4484 - IsTuple ➜
number extends T['length']
因為 Tuple 的
length
是確切數字(不是單純的number
),因此若number extends T['length']
成立則代表T
不是 Tuple。
4518 - Fill ➜
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T['length'],
Count extends any[] = [],
Flag extends boolean = Count['length'] extends Start ? true : false
> = Count['length'] extends End
? T
: T extends [infer R, ...infer Rest]
? Flag extends false
? [R, ...Fill<Rest, N, Start, End, [...Count, 0]>]
: [N, ...Fill<Rest, N, Start, End, [...Count, 0], true>]
: T
當
Count['length']
和Start
相同時,Flag
標示為true
,代表接下來遞迴需要將R
替換成N
。
當Count['length']
和End
相同時,代表替換已經結束,直接回傳剩餘的陣列即可(不需要特別將Flag
換回false
繼續跑遞迴)。
5117 - Without ➜
R extends TupleToUnion<U>
? Without<Rest, U, A>
: Without<Rest, U, [...A, R]>
這樣的判斷可以改寫成這樣:
typescriptWithout<Rest, U, R extends TupleToUnion<U> ? A : [...A, R]>
5317 - LastIndexOf ➜
type LastIndexOf<T extends unknown[], U> = T extends [...infer Rest, infer Last]
? Equal<Last, U> extends true
? Rest['length']
: LastIndexOf<Rest, U>
: -1
剛好
Rest['length']
取到的值會比T['length']
少 1,所以可以直接拿來當作index
參考。
5821 - MapTypes ➜
type MapTypes<T extends Record<string, any>, R extends {
mapFrom: any
mapTo: any
}> = {
[K in keyof T]: T[K] extends R['mapFrom']
? R extends { mapFrom: T[K] }
? R['mapTo']
: never
: T[K]
}
因為
R
傳入的參數有可能是 Union,因此透過R extends { mapFrom: T[K] }
可以限制這行的R
是哪一項,否則結果也會產生 Union。
10969 - Integer ➜
type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never
bigint
一定是 Integer。
16259 - ToPrimitive ➜
type ToPrimitive<T> = T extends object
? { [K in keyof T]: ToPrimitive<T[K]> }
: T extends { valueOf(): infer R }
? R
: never
T extends { valueOf(): infer R }
是根據valueOf
的值取得推斷的型別R
,string
、number
...等等都有valueOf
,所以never
不會跑到。
17973 - DeepMutable ➜
type A = DeepMutable<Test2>
type C = A['c']
type E = C['e']
若物件太深,型別無法全部顯示清楚的話,可以藉由新建
type
並取得原type
的其屬性查看內部型別。
25270 - Transpose ➜
type Transpose<
M extends number[][],
R = M['length'] extends 0 ? [] : M[0]
> = {
[X in keyof R]: {
[Y in keyof M]:X extends keyof M[Y] ? M[Y][X] : never
}
}
物件的 key 為數字時可作為陣列。