type-challenge
刷題
內容接前文深入學習typescript type型別type-challenge
挑戰地址
easy
一眼過的部分
type MyPick<T, K extends keyof T> = {[k in K]: T[k]};
type MyReadonly<T> = {readonly [k in keyof T]: T[k]};
type TupleToObject<T extends readonly (string|number|symbol)[]> = {[k in T[number]]: k};
type First<T extends any[]> = T extends [infer Head, ...unknown[]] ? Head : never;
type Length<T extends readonly unknown[]> = T["length"]; // 題目要求readonly陣列
type If<C extends boolean, T, F> = C extends true ? T : F;
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];
type Push<T extends unknown[], U> = [...T, U];
type Unshift<T extends unknown[], U> = [U, ...T];
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
Exclude
原題地址
type MyExclude<T, U> = T extends U ? never : T;
這裡有個遺漏的知識點...分配條件型別
當type
引數聯合型別時,內部其實是作迴圈處理的。以Exclude
為例,分配條件型別的實際處理如下
MyExclude<'a'|'b'|'c', 'a'|'b'> =
('a' extends 'a'|'b' ? never : 'a') |
('b' extends 'a'|'b' ? never : 'b') |
('c' extends 'a'|'b' ? never : 'c')
Await
原題地址
type PromiseLike<T = unknown> = {then: (cb: (arg: T)=>unknown) => unknown};
type MyAwaited<T extends PromiseLike> = T extends PromiseLike<infer U> ? (U extends PromiseLike ? MyAwaited<U> : U) : never;
需要實現await
,即const result = await PromiseVal
的await
。
此處實現方式類似Promise A+協議中的resolvePromise
部分,之所以以自定義的PromiseLike
作為Promise
的判斷條件,是因為在resolvePromise
中,判斷一個物件是否是Promise,是以typeof promise.then === "function"
作為判斷條件,這保證了不同pollyfill實現的Promise函式之間可以相互進行鏈式呼叫,且滿足PromiseLike
的物件都能用async...await
語法。
Include
原題地址
type Eq<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 H, ...infer Rest]
? (Eq<H, U> extends false ? Includes<Rest, U>: true)
: false;
Include
主體部分還好,最麻煩的是Equal
部分,一開始寫的Equal
如下
type Eq<X, Y> = X extends Y ? (Y extends X ? true : false) : false;
// 這個是錯的,測試用例如下
type check = Eq<boolean, true> // boolean
這裡忽略了boolean
其實是個複合型別,根據前面分配條件型別提到的,作為引數傳遞時會進行遍歷
type check = Eq<boolean, true> // boolean
// ⬇️
// boolean -> true|false
// ====> Eq<true, true>|Eq<false, true> -> true|false -> boolean
直接翻看了'@type-challenges/utils'
的庫,發現它是利用function
的定義繞過物件的extends
判斷。。這一點比較具有啟發性
type Eq<X, Y> =
// 這裡沒有直接進行X和Y的比較,那樣會觸發分配條件型別
// 因此藉助範型的變數T作為橋樑進行比較
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
習慣了常規程式語言的語法後,很容易忽略【分配條件型別】這條規則,可以借用中間變數的思想,間接繞過直接的extends
判定