TypeScript类型体操挑战(四)

153 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

中等

Readonly 2

挑战要求

在线示例

type MyReadonly2<T, K extends keyof T = keyof T> = Readonly<Pick<T, K>> & Omit<T, K>
  • 首先使用PickK类型的键挑选出来组成新的类型,然后通过Readonly变为只读的对象,这样就达到了将部分属性变为readonly的目的了
  • 那么另外一部分属性就使用Omit来创建新的类型
  • 最后通过&操作符组成交叉类型

深度 Readonly

挑战要求

在线示例

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends Record<string, unknown> ? DeepReadonly<T[P]> : T[P];
}
  • 主要在于判断T[P]的值类型是不是对象,是的话要进行递归处理

元组转合集

挑战要求

在线示例

type Mapping<T extends readonly any[]> = {
  [P in T[number]]: P;
}

type TupleToUnion<T extends readonly any[]> = keyof Mapping<T>;
  • 先将数组中的元素组装成一个对象类型,也就是Mapping
  • 然后通过keyof就可以获取到数组元素的联合类型了

我在解答区看到的👍比较多的答案:

type TupleToUnion<T> = T extends Array<infer Item> ? Item : never;

type TupleToUnion<T extends readonly any[]> = T[number];

可串联构造器

挑战要求

在线示例

type Chainable<T = {}> = {
  option<K extends string, V>(key: Exclude<K, keyof T>, value: V): Chainable<T & Record<K, V>>,
  get(): T;
}

分成几步来进行分析:

type Chainable<T = {}> = {
  option<K extends string, V>(key: K, value: V): Chainable<T & Record<K, V>>,
  get(): T;
}
  • 每次调用option方法时,需用将keyvalue的类型组成一个对象类型,为了获取到这两个字段的类型,所以要给option()添加泛型KV,再用Record进行组合。
  • 泛型T是用来累加键值对类型的,而每次调用时,需要跟上次的类型进行合并,所以使用了操作符&变为交叉类型
  • 最后就是每次调用option()返回的都是当前键值对累加的对象类型,这样就可以持续将该对象类型顺着链条传递下去了

由于同样的key只能使用一次,所以要对泛型K进行约束,也就有了最终的形式:

type Chainable<T = {}> = {
  // 使用 Exclude 进行排除重复的 key
  option<K extends string, V>(key: Exclude<K, keyof T>, value: V): Chainable<T & Record<K, V>>,
  get(): T;
}

答案参考自解答区,作者还对该类型进行了扩展,感兴趣的可以了解下。