Skip to content

类型体操 01

INFO

以下的题目全部出自 Type Challenge,如果想刷题的话可以安装 VSCode 的插件。

获取函数返回值

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

实现 Omit

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

实现 Pick

ts
type MyPick<T, K extends keyof T> = {
  [P in keyof T as P extends K ? P : never]: T[P]
}

实现 Readonly

ts
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

将 Tuple 转为对象

ts
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
  [P in T[number]]: P
}

获取数组第一个元素的类型

ts
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]

注意空数组的判断就行。

获取元组的长度

ts
type Length<T extends readonly any[]> = T['length']

实现内置的 Exclude

ts
type MyExclude<T, U> = T extends U ? never : T

获取 Promise 的返回值

ts
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer R>
  ? R extends PromiseLike<any>
    ? MyAwaited<R>
    : R
  : never

注意上面用的是的 PromiseLike 而不是 Promise,主要是为了避免用户自己实现的 Promise 不完全符合标准,比如说以下的测试:

ts
type T = { then: (onfulfilled: (arg: number) => any) => any }

实现 IF

ts
type If<C extends boolean, T extends any, F extends any> = C extends true
  ? T
  : F

实现 Concat

ts
type Tuple = readonly unknown[]
type Concat<T extends Tuple, U extends Tuple> = [...T, ...U]

实现 Include

ts
type IsEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U
  ? 1
  : 2
  ? true
  : false

type Includes<T extends readonly any[], U> = T extends [
  infer First,
  ...infer Rest
]
  ? IsEqual<First, U> extends true
    ? true
    : Includes<Rest, U>
  : false

这道题难度估计是划错了,说是简单,实现起来挺多细节,具体的坑可以参考于此,主要就是nullundefined在 TS 中互相赋值的问题。

实现 Push

ts
type Push<T extends unknown[], U> = [...T, U]

实现 Unshift

ts
type Unshift<T extends unknown[], U> = [U, ...T]

实现 Parameter

ts
type MyParameters<T extends (...args: any[]) => any> = T extends (
  ...args: infer R
) => any
  ? R
  : never

获取函数返回值

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

实现 Omit

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

对象部分属性只读

ts
type MyReadonly2<T, K extends keyof T = keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P]
} & { readonly [P in K]: T[P] }

对象属性只读(递归)

ts
type DeepReadonly<T> = {
  readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]>
}

type DeepReadonly2<T> = keyof T extends never
  ? T
  : { readonly [k in keyof T]: DeepReadonly<T[k]> }

这道题的难点在于 DeepReadonly2 为什么通过不了下面的测试:

ts
type X2 = { a: string } | { b: number }
type Expected2 = { readonly a: string } | { readonly b: number }

元组转合集

ts
type TupleToUnion<T> = T extends (infer P)[] ? P : never

可串联构造器

ts
type Chainable<T = {}> = {
  option: <K extends string, V>(
    key: K extends keyof T ? never : K,
    value: V
  ) => Chainable<Omit<T, K> & Record<K, V>>
  get: () => T
}

注意重复属性赋值的问题。

获取最后一个元素

ts
type Last<T extends any[]> = [any, ...T][T['length']]

这题奇葩就奇葩在第一个any上,要不T['length']会超过索引,但 TS 中又不能使用T['length'] - 1这种表达式,有些时候 TS 的语法还是稍显奇怪的。