乐趣区

关于typescript:type-challengeeasy-部分

type-challenge刷题

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' ? never : 'a') |
  ('b' extends 'a' ? never : 'b') |
  ('c' extends 'a' ? never : 'c') |
  ('a' extends 'b' ? never : 'a') |
  ('b' extends 'b' ? never : 'b') |
  ('c' extends 'b' ? never : 'c')

这里应该是作了两层循环,之前看有些文章里说是解决成这种

// 谬误的了解
MyExclude<'a'|'b'|'c', 'a'|'b'> = 
  ('a' extends 'a'|'b' ? never : 'a') |
  ('b' extends 'a'|'b' ? never : 'b') |
  ('c' extends 'a'|'b' ? never : 'c') 

例如咱们要实现反过来的Exclude,代码如下:

// ReverseExclude<'a'|'b', 'a'|'b'|'c'>  -> 'c'
type ReverseExclude<T, U> = U extends T ? never : U;

如果只有一层循环,依照如下解决只能失去 never 类型,显然和事实不符

// 谬误的了解
ReverseExclude<'a'|'b', 'a'|'b'|'c'> = 
  ('a'|'b'|'c' extends 'a' ? never : 'a'|'b'|'c') |
  ('a'|'b'|'c' extends 'b' ? never : 'a'|'b'|'c')

但按理说解析器会和具体代码业务解耦,ts类型解析时应该不会联合上下文去判断多个参数之间的关系。

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 PromiseValawait

此处实现形式相似 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 断定

退出移动版