TypeScript 类型操作用法

103 阅读2分钟

常用运算

  1. 条件 extends ?
type First<T> = T extends [infer R, ...any[]] ? R : never
  1. 约束 extends
type TupleToObject<T extends readonly any[]> = { [P in T[number]]: P }

extends在类型操作中有narrow的作用,1、在=右边可以用作条件,判断extends的左边是否可赋值(兼容)右边 ;2、在等号左边,可以约束左边的边界。

  1. 推导 infer
type First<T> = T extends [infer R, ...any[]] ? R : never

一般情况下,infer都会和infer进行搭配做提取推导操作,可参看下面的模式匹配做提取,当然其实可以结合递归实现更复杂的操作。

  1. 联合 |
type Numeric = number | string
  1. 交叉 &
type T = { a: string } & { b: boolean }
  1. 索引查询 keyof
type Key<T> = keyof T
  1. 索引访问 []
type Length<T extends readonly any[]> = T['length']

通常情况下,用索引访问时,取得是对应键对应的类型。但对于数组来说,有一点特殊:

type TArr = ['0', '1', '2']

type Ta = TArr[number] // '0' | '1' | '2'
type Ta2 = TArr['length'] // 3

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];
 
type Person = typeof MyArray[number];
       
// type Person = {
//    name: string;
//    age: number;
// }

  1. 索引遍历 in (一般和keyof搭配)
type Readonly<T> = { readonly [P in keyof T]: T[P] }
  1. 索引重映射 as
type MapType<T> = {
  [Key in keyof T as `${Key & string}${Key & string}`]: [T[Key], T[Key]]
}

常用模式

  1. 模式匹配做提取
  type GetParameters<Func extends Function> =
      Func extends (...args: infer Args) => unknown ? Args : never;

  type ParametersResult = GetParameters<(name: string, age: number) => string>
  1. 重新构造做变换
type CapitalizeStr<Str extends string> =
  Str extends `${infer First}${infer Rest}`
  ? `${Uppercase<First>}${Rest}` : Str;

type CapitalizeResult = CapitalizeStr<'tang'>
  1. 递归复用做循环
type TrimLeft<T extends string> = T extends ` ${infer R}` ? TrimLeft<R> : T;
type value = TrimLeft<"      value">
  1. 数组长度做计数
type BuildArray<
    Length extends number,
    Ele = unknown,
    Arr extends unknown[] = []
    > = Arr['length'] extends Length
    ? Arr
    : BuildArray<Length, Ele, [...Arr, Ele]>;

type Add<Num1 extends number, Num2 extends number> =
    [...BuildArray<Num1>, ...BuildArray<Num2>]['length'];


type AddResult = Add<32, 25>

实现内置类型

type TPartial<T> = {
  [P in keyof T]?: T[P];
}

type TRequired<T> = {
  [P in keyof T]-?: T[P]
}

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

type TPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

type TRecord<K extends keyof any, T> = {
  [P in K]: T
}

type TExclude<T, U> = T extends U ? never : T

type TExtract<T, U> = T extends U ? T : never

type TOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

type TAwaited<T> =
  T extends null | undefined
  ? T
  : T extends object & { then(onfulfilled: infer F): any }
  ? F extends ((value: infer V, ...args: any) => any)
  ? Awaited<V>
  : never
  : T

参考

类型体操的9种类型运算、4种类型套路总结