类型体操之基本原理

179 阅读2分钟

1. 什么是类型体操

类型体操的本质就是在自己的大脑中训练各种各样的类型,从而使自己更加灵活的使用各种各样的类型

1.1 体操的基本原理

// 基础的体操
// type A = 1
// type B = 1 | 2
// type Result = A extends B ? true : false

// 复杂一点的体操
// type A = 1
// type B = 1 | 2
// type C = 3
// type D = 3 | 4

// // 表示 if (A <= B) and (C <= D) ...
// type Result = A extends B ? (C extends D ? 'true, true' : 'true, false') : C extends D ? 'false, true' : 'false, false'

// 空元组
type A = []
type IsEmptyArray<Arr extends unknown[]> = Arr['length'] extends 0 ? true : false
type Result = IsEmptyArray<A>

// 非空元组
// 方式一
type IsNotEmptyArray<Arr extends unknown[]> = Arr['length'] extends 0 ? false : true
type Result1 = IsNotEmptyArray<[1, 2]>
// 方式二
// type NotEmpty<Arr extends unknown[]> = Arr extends [...unknown[], unknown] ? true : false
// X 可以为空
type NotEmpty<Arr extends unknown[]> = Arr extends [...infer X, infer Y] ? true : false

type Result2 = NotEmpty<[1]>

// 递归
type B = ['ji', 'ni', 'tai', 'mei']
type Reverse<Arr extends unknown[]> = Arr extends [...infer Rest, infer Last] ? [Last, ...Reverse<Rest>] : Arr
type Result3 = Reverse<B>
// Result is ['mei', 'tai', 'ni', 'ji']

// 模式匹配 + infer '引用'
type Tuple = ['ji', 'ni', 'tai', 'mei']
type Result4 = Tuple extends [infer First, ...infer Last] ? First : never
// 如果不想使用Last, 可以只写上类型
type Result5 = Tuple extends [infer First, ...string[], infer Last] ? Last : never

type C = ['ji', 'ni', 'mei']
/**
 * 等价于下面
 * type C = { 0: 'ji', 1: 'ni', 2: 'mei', length: 3 }
 * 任何数组都是一个对象
 */

1.2 元组的基本体操

// 扩展元组
// type A = [1, 2, 3, 4]
// type B = [...A, 2]

type A = [1, 2, 3, 4]
type B = [5, 6, 7]
type C = [...A, ...B]

type D = [1, 2, 3, 4]
// 注意 1,2,3,4 都是类型,不是值
type Last<T extends unknown[]> = T extends [...unknown[], infer L] ? L : never
type NotLast<T extends unknown[]> = T extends [...infer X, unknown] ? X : never
type Result = Last<D>
type Result1 = Last<[]>
type Result2 = NotLast<D>

// 这种写法会报错
// ts 没有提供 - 操作符
// type Last1<T extends unknown[]> = T[T['length'] - 1]

1.3 字符串的基本体操

// type A = 'fang'
// type B = Capitalize<A>

// type C = 'ji' | 'ni' | 'tai' | 'mei'
// // 当联合类型和泛型结合的时候会对每一个类型做分配率
// type D = Capitalize<C>

// 以下几个全是ts内置的方法
/**
 * 全变成大写
 */
type Uppercase<S extends string> = intrinsic
/**
 * 全变成小写
 */
type Lowercase<S extends string> = intrinsic
/**
 * 首字母大写
 */
type Capitalize<S extends string> = intrinsic
/**
 *首字母小写
 */
type Uncapitalize<S extends string> = intrinsic

// 模板字符串
// type A = 'ji'
// type B = 'ni'
// type C = 'tai'
// type D = 'mei'
// type X = `${A} ${B} ${C} ${D}`

// 获取字符串的第一项
type A = 'ji ni tai meix'
type First<T extends string> = T extends `${infer F}${string}` ? F : never
type Result = First<A> // j

// 如何获取字符串的最后一个?
// 我们可以获取元组的最后一项
//字符串可以转化为元组
// 我们获取字符串的最后一项

type LastOfTuple<T extends unknown[]> = T extends [...infer _, infer L] ? L : never
type StringToTuple<S extends string> = S extends `${infer F}${infer R}` ? [F, ...StringToTuple<R>] : []
type LastOfString<S extends string> = LastOfTuple<StringToTuple<S>>
type Result2 = LastOfString<A>

1.4 infer

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type

type X = Flatten<Array<number>>

// https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#handbook-content

1.5 递归的层数限制

type A = [
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei',
  'ji',
  'ni',
  'tai',
  'mei'
  // 'ji'
]

type Reverse<Arr extends unknown[]> = Arr extends [...infer Rest, infer Last] ? [Last, ...Reverse<Rest>] : Arr
// 递归最多48层
type Result3 = Reverse<A>

1.6 字符串转元组和联合

// 字符串转联合
type StringToUnion<S extends string> = S extends `${infer First}${infer Rest}` ? First | StringToUnion<Rest> : never
type Result = StringToUnion<'jinitaimei'>
// 注意: 联合类型自动去重

// 字符串转元组
type StringToTuple<S extends string> = S extends `${infer First}${infer Rest}` ? [First, ...StringToTuple<Rest>] : []
type Result1 = StringToTuple<'jinitaimei'>