TS 类型体操实现

124 阅读2分钟

前言

最近想复习下 TS,所以想着练习一下 TS 类型体操,先实现了几个,后续可能还会继续更新!感兴趣的朋友也可以去 github 地址 试试。

实现 Pick

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}

// T => Todo, keyof T 返回的是联合类型; K extend keyof T
// P in K // 遍历 K 联合类型,P 为联合类型中的 item, T[P] 为 Todo[P]
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

实现 ReadyOnly

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

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

实现 元组转换为对象

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

// typeof tuple = readonly ['tesla', 'model 3', 'model X', 'model Y']

// T[number] 得到的是一个联合类型的
// T[number] === 'tesla' | 'model 3' | 'model X' | 'model Y'
type TupleToObject<T extends readonly any[]> = {
  [K in T[number]]: K
}

实现获取 第一个元素

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
type head3 = First<[]> // expected to be 3

type First<T extends any[]> = T extends [] ? never : T[0]; // 如果是空数组返回 never
// type First<T extends any[]> = T['length'] extends 0 ? never : T[0]; // 如果是空数组返回 never

实现 获取元组长度

type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5

// ts 类型也可以通过 length 属性直接获取到数组长度
type Length<T extends readonly any[]> = T['length'];

实现 Exclude

type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'

// MyExclude 的比较方式, T 是联合类型,所以会对联合类型的每一个属性都比较一次( 我是这样理解的 😅 )
// 1. 'a' extends 'a' ? never : 'a';
// 2. 'b' extends 'a' ? never : 'b';
// 3. 'c' extends 'a' ? never : 'c';
type MyExclude<T, U> = T extends U ? never : T;