关于typescript:type-challenge-middle-部分一

44次阅读

共计 3460 个字符,预计需要花费 9 分钟才能阅读完成。

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

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

一眼过的局部

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

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

DeepReadonly

这块内容还是【调配条件类型】没把握好,能够参看另一篇文档.

Chinable

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

type Chainable<T extends {} = {}> = {option<P extends string, Q>(key: P extends keyof T ? never : P, value: Q): Chainable<MyAssign<T, Record<P, Q>>>
  get(): T}

这里难点有两个:

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

      const result2 = a
        .option('name', 'another name')
        // @ts-expect-error
        .option('name', 'last name')
        .get()
    • name 属性赋值后,再次赋值 name 属性就会报错,但 ts type 没法间接用 判断,大多只能利用其 补集 进行判断
    • 参数侧 option<P extends string, Q> 必定是没法用条件语句进行判断,如果这么写option<P extends (P extends keyof T ? never : P), Q>,会间接报错 <span style=”color: red”>Type parameter ‘P’ has a circular constraint.</span>
    • 因而,只能在函数的参数中进行断定option<P extends string, Q>(key: P extends keyof T ? never : P, value: Q)
  2. 新增加的属性,须要实现相似 Assign 的办法

    • 这里不能用 Object.assign 办法的实现形式,本来 Object.assign 的实现形式如下
    • assign<T extends {}, U>(target: T, source: U): T & U;
    • 这种实现形式带来了一个问题,即 const b = Object.assign(aa, { name: 11});typeof b 的后果为{name: string;} & {name: number;},显然不合乎需要,因而本人实现
    • type MyAssign<T extends {}, Q extends {}> = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}

PromiseAll

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

这一题如果依照测试用例缓缓拼的话,也不算太难,最开始拼凑进去的写法如下:

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

declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<PromiseValues<T>>;

这里遇到了两个问题:

  1. PromiseAll<T extends any[]>(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 string> = S extends `${infer H}${infer T}` ? `${Uppercase<H>}${T}` : S;

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

网上没找到相干文章,问了 chat-gpt,它不苟言笑通知我 infer H 这种格局的语法只能匹配一个字母,并给了我上面的例子

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

type Test = Substring2<'hello'>; // chat-gpt 说后果是 'hl',理论跑是 'hllo'

多测试了几个例子,诸如

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

发现 infer 会以 第一个找到的匹配模式为规范 ,相似Array.find 办法,这样的话,了解 MyCapitalize 的实现就容易多了

正文完
 0