type challenge middle 部分(一)

续接上篇:[easy 部分]https://segmentfault.com/a/1190000043777580

熟悉了基本知识点之后,middle整体比较顺畅,就是题目太多,更多是知识点的查漏补缺。

一眼过的部分

type MyReturnType any> = 
    T extends (...args: any[]) => infer R ? R : never;
// 实现Omit
type Exclude = T extends K ? never : T;
type MyOmit = {[k in Exclude]: T[k]}
// 指定部分readonly,此处假设已经实现了MyOmit..
type MyReadonly2 = {readonly [k in K]: T[k]} & MyOmit
type DeepReadonly = {
  readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly;
};
type TupleToUnion = T[number];
type Last = T extends [...any[], infer U] ? U : never;
type Pop = T extends [...infer U, any] ? U : T;
type LookUp = U extends {type: T} ? U : never;
// Trim相关,先实现Space类型
type Space = ' '|'\n'|'\t';
type TrimLeft = S extends `${Space}${infer U}` ? TrimLeft : S;
type Trim = S extends `${Space}${infer U}`|`${infer U}${Space}` ? Trim: S;

type Replace = From extends '' ? S : (S extends `${infer H}${From}${infer T}` ? `${H}${To}${T}` : S);

DeepReadonly

这块内容还是【分配条件类型】没掌握好,可以参看另一篇文档.

Chinable

type MyAssign = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}

type Chainable = {
  option

(key: P extends keyof T ? never : P, value: Q): Chainable>> get(): T }

这里难点有两个:

  1. 重复赋值时,怎么让其报错

      const result2 = a
        .option('name', 'another name')
        // @ts-expect-error
        .option('name', 'last name')
        .get()
    • name属性赋值后,再次赋值name属性就会报错,但ts type没法直接用判断,大多只能利用其补集进行判断
    • 参数侧option

      肯定是没法用条件语句进行判断,如果这么写option

      ,会直接报错Type parameter 'P' has a circular constraint.

    • 因此,只能在函数的参数中进行判定option

      (key: P extends keyof T ? never : P, value: Q)

  2. 新添加的属性,需要实现类似Assign的方法

    • 这里不能用Object.assign方法的实现方式,原本Object.assign的实现方式如下
    • assign(target: T, source: U): T & U;
    • 这种实现方式带来了一个问题,即const b = Object.assign(aa, { name: 11 });typeof b的结果为{ name: string;} & { name: number;},显然不符合需求,因此自己实现
    • type MyAssign = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}

PromiseAll

// 最终答案
declare function PromiseAll(values: readonly [...A]): Promise<{
  [key in keyof A]: Awaited
}>

这一题如果按照测试用例慢慢拼的话,也不算太难,最开始拼凑出来的写法如下:

type PromiseValue = T extends Promise ? PromiseValue : T;
type PromiseValues = T extends [infer H, ...infer E] ? PromiseValues]>: ([] extends R ? PromiseValue[] : R);

declare function PromiseAll(values: readonly [...T]): Promise>;

这里遇到了两个问题:

  1. PromiseAll(values: readonly [...T])这里为什么要用readonly

    • 其中有个测试用例是这样的PromiseAll([1, 2, Promise.resolve(3)] as const)
    • 自己没查到as const是什么意思,问了chat-gpt,给的答复是这里的as const是将数据变为只读,这样的话很明确了,readonlyT不是T的子类,我们要将values提升为readonly的类型
  2. 是否可以用对象表示数组

      type A = [number, string, {a: number}]
      type B = {
        [k in keyof A]: A[k]
      }
      
      type C = A extends B ? (B extends A ? true: false): false; // true!!!
    • 这种复制对象的方式,可以用在数组上

MyCapitalize

这一题一开始没想多少,直接写出了正确答案

type MyCapitalize = S extends `${infer H}${infer T}` ? `${Uppercase}${T}` : S;

但回头想到了一个问题,infer H为什么只匹配了第一个字母,正则具有贪婪性,infer不具备吗?

网上没找到相关文章,问了chat-gpt,它一本正经告诉我infer H这种格式的语法只能匹配一个字母,并给了我下面的例子

type Substring2 = S extends `${infer A}${infer _}${infer B}` ? `${A}${B}` : never;

type Test = Substring2<'hello'>; // chat-gpt说结果是'hl',实际跑是'hllo'

多测试了几个例子,诸如

type UpperFirstPart = S extends `${infer H} ${infer T}` ? `${Uppercase} ${T}` : S;
type Test = UpperFirstPart<"hello world">; // HELLO world

发现infer会以第一个找到的匹配模式为标准,类似Array.find方法,这样的话,理解MyCapitalize的实现就容易多了

你可能感兴趣的:(typescript前端)