[TypeScript] 类型挑战 - medium难度个人题解(1)

557 阅读2分钟

题目地址:github

在线编辑器:TypeScript Playground

1. ReturnType

type MyReturnType<Fn extends Function> = Fn extends (...args: any) => infer R ? R : never;

2. Omit

type MyExclude<T, K> = T extends K ? never : T;
type MyOmit<T, K extends keyof T> = {
  [P in MyExclude<keyof T, K>]: T[P];
}

3. Readonly2

允许选择指定的key进行readonly化

type MyReadonly2<T, K extends keyof T> = {
  readonly [P in K]: T[P];
} & {
  [P in MyExclude<keyof T, K>]: T[P];
}

4. DeepReadonly

对嵌套属性也进行readonly化

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends number | string | symbol ? T[P] : DeepReadonly<T[P]>;
}

类型也可以递归

5. TupleToUnion

type TupleToUnion<A extends any[]> = A[number] 

6. Chainable

实现可以链式调用的类型结构,其中包含 option(key, value) get() 两个方法,option 的参数是对象的属性和值,get 用于得到最后的结果。

主要是需要实现可以达到这样效果的类型声明,不需要写逻辑

interface Chainable<T = {}> {
  // `K extends string` 是让ts推导 `key` 类型的时候将类型收窄为字面量类型
  // `[P in K]` 是将推导的 `K` 看做一个联合类型,然后使用 `in` 拿到具体的类型,用作
  // 键名,这样最终的类型中才会出现明确的键名
  option: <K extends string, V>(key: K, value: V) => Chainable<T & {
    [P in K]: typeof value
  }>,
  get: () => T
}

可以链式调用就意味着这个 option 方法返回了这个类型,所以需要提供一个泛型参数 T ,其作用为:1. 接收不断调用 option 传递的 key value ,构造新的类型继续作为下一个 T ;2. 作为 get 方法返回值。

7. Last

获取数组最后一个值的类型

type Last<A extends any[]> = [undefined, ...A][A['length']];

这题我没做出来,没转过来弯。看到比较巧妙的思路就是在数组前面加一个元素,那原来的 length 就是新数组的最后一个index

8. Pop

实现数组的pop方法,返回pop后的数组类型

type Pop<A extends any[]> = A extends [...infer R, infer L] ? R : undefined;
type Shift<A extends any[]> = A extends [infer F, ...infer R] ? R : undefined;

shift同理

上一题的Last也可以用类似的方式,比如直接返回 L 。需要注意的是 infer 关键字必须用在条件类型中 extends 后面

9. PromiseAll

取到 Promise.all() 返回值的类型

type GetPromiseType<P> = P extends Promise<infer R> ? R : P;
type PromiseAll<P extends any[], R extends any[] = []> = P extends [infer F, ...infer Rest]
  ? PromiseAll<Rest, [GetPromiseType<F>, ...R]>
  : Promise<R>;

还是递归,暂时没想到其他方法。为了能够拿到每一项的类型,就递归取第一个元素进行判断

10. LookUp

从联合类型(限定类型中存在 { type: string } )中查找符合要求的类型

type LookUp<T, P> = T extends { type: P } ? T : never;

'a' | never === 'a',任何其他类型和never进行 | 返回其本身

11. TrimLeft

对字符串进行trim left处理

type TrimLeft<S extends string> = S extends `${infer F}${infer R}`
  ? F extends ' '
    ? TrimLeft<R>
    : S
  : S;

字符串也可以使用条件类型进行解构,一次一个字符。 字符串可以根据合理模式进行自由解构

12. Trim

type ReverseString<
  S extends string,
  R extends string = ''
> = S extends `${infer F}${infer Rest}`
  ? ReverseString<Rest, `${F}${R}`>
  : R;

type Trim<S extends string> = ReverseString<TrimLeft<ReverseString<TrimLeft<S>>>>

想法就是先trim lefe,然后翻转再trim left,最后再翻转回来