TypeScript类型体操合集

87 阅读5分钟

前言

现在前端项目ts还是用的比较多的,我之前也是只有用点基本的,比较菜,所以这次刷下ts类型体操提升下自己,我只刷了简单和中等的,太多了,干不动了。

简单

实现Pick

实现 TS 内置的 Pick<T, K>,但不可以使用它

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

/* 
  A = {
    title: string;
    completed: boolean;
  }
*/
type A = MyPick<Todo, 'title' | 'completed'>

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

实现 Readonly

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

元组转换为对象

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

type TupleToObject<T extends readonly (keyof any)[]> = {
    [P in T[number]]:P
}
type A = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

数组的第一个元素

实现一个通用First<T>,它接受一个数组T并返回它的第一个元素的类型。

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

type A = First<[string,number]>

获取元组的长度

元组类型是另一种Array类型,它确切地知道它包含多少元素,以及在特定位置包含哪些类型

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

实现Exclude

实现内置的 Exclude<T, U> 类型,但不能直接使用它本身。

从联合类型 T 中排除 U 中的类型,来构造一个新的类型。

例如:

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

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

实现 Awaited

假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

例如:Promise<ExampleType>,请你返回 ExampleType 类型。

type ExampleType = Promise<string>
type MyAwaited<T extends Promise<any>> = T extends Promise<infer L>?L:never
type Result = MyAwaited<ExampleType> // string

If

type A = If<true, 'a', 'b'>  // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'

type If<T extends boolean,A,B> = T extends true ?A:B

Concat

type Concat<T extends any[], U extends any[]> = [...T, ...U]
type Result = Concat<[1], [2]> // expected to be [1, 2]

Includes

// 将元组转换一个value为true的对象
type Includes<T extends any[], U> = {
  [K in T[number]]: true
}[U] extends true
  ? true
  : false

中等

获取函数返回类型

不使用 ReturnType 实现 TypeScript 的 ReturnType<T> 范型。

type MyReturnType<T> = T extends (...argv:any[]) => infer T ? T :never

实现 Omit

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

实现 Readonly 2

实现一个通用MyReadonly2<T, K>,它带有两种类型的参数TK

K指定应设置为Readonly的T的属性集。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly<T>一样。

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

// upgrade
type MyReadonly2<T, K extends keyof T = keyof T> = {
    readonly [P in K]: T[P]
  } & T;

元组转合集

type Arr = ['1', '2', '3']

const a: TupleToUnion<Arr> // expected to be '1' | '2' | '3'

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

最后一个元素

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

type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1

type Last<T extends any[]> = T extends [...any[],infer F]?F:never

Pop

删除元组最后一个

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

type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]


type Pop<T extends any[]> = T extends [... infer R, infer L] ? R :never

实现shift

删除元组第一个

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

type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]


type Shift<T extends any[]> = T extends [infer R, ...infer L] ? L :never

Promise.all


const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

// expected to be `Promise<[number, number, string]>`
const p = Promise.all([promise1, promise2, promise3] as const)

declare function PromiseAll<T extends readonly unknown[]>(
  args: readonly [...T]
): Promise<{ [P in keyof T]: T[P] extends Promise<infer R> ? R : T[P] }>

Type Lookup

根据其属性在并集中查找类型。 期望LookUp<Dog | Cat, 'dog'>获得DogLookUp<Dog | Cat, 'cat'>获得Cat

interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`

type LookUp<T,U> = T extends {type:U}?T:never

Trim Left

删除开始的空格 example:

type trimed = TrimLeft<'  Hello World  '> // expected to be 'Hello World  '

type TWhiteSpace = ' ' | '\n' | '\t'

type TrimLeft<S extends string> = S extends `${TWhiteSpace}${infer R}`
  ? TrimLeft<R>
  : S

Trim

去除两侧空格 example:

type trimed = Trim<'  Hello World  '> // expected to be 'Hello World'

type TWhiteSpace = ' ' | '\n' | '\t'

type Trim<S extends string> = S extends (`${TWhiteSpace}${infer R}`|`${infer R}${TWhiteSpace}`)
  ? Trim<R>
  : S

Capitalize

实现 Capitalize 将第一个字母转换为大写

type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'

type Capitalize<S extends string> = S extends `${infer L}${infer R}`?`${Uppercase<L>}${R}`:S

Replace

替换字符串中对应的值

type replaced = Replace<'types are fun!', 'fun', 'awesome'> // expected to be 'types are awesome!'

type Replace<S extends string,F extends string,T extends string> = S extends ''?S:S extends `${infer L}${F}${infer R}`?`${ L}${T}${R}`:S

函数追加参数

type Fn = (a: number, b: string) => number

type Result = AppendArgument<Fn,  boolean> 
// 期望是 (a: number, b: string, x: boolean) => number

type AppendArgument<F extends (...s:any[]) => any,A> = F extends (...s:infer T) => infer R?(...s:[...T,A]) => R:never

Permutation联合转换为包含联合排列的数组

实现将联合类型转换为包含联合排列的数组的排列类型


type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
//分布式 确定 第一个元素,再把第一个元素除去,递归确定剩余元素
type Permutation<T, P = T> = [T] extends [never]
  ? []
  : T extends any
  ? [T, ...Permutation<Exclude<P, T>>]
  : never

获取字符串的长度

type LengthOfString<S extends string, T extends any[] = []> = S extends `${infer L}${infer R}`
  ? LengthOfString<R, [...T, L]>
  : T['length']

Flatten数组扁平处理

type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]

type Flatten<T extends any[]> = T extends [infer L,...infer R]? 
[...(L extends any[]?Flatten<L>:[L]),...Flatten<R>]:T

Append to object添加新属性

type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4> // expected to be { id: '1', value: 4 }

type EachObject<T> = {
  [P in keyof T]: T[P]
}
type AppendToObject<T extends Record<string,any>,K extends string,V> =EachObject<{
  [P in keyof T as P extends K?never:P]: T[P]
}&{[U in  K]:V}>

Absolute

实现 Absolute。 接收字符串、数字或 bigint 的类型。 输出应该是一个正数字符串

type Test = -100;
type Result = Absolute<Test>; // expected to be "100"
type Numbers = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type Absolute<T extends number | string | bigint> = `${T}` extends `${infer L}${infer R}`?`${L}` extends `${Numbers}`?`${L}${Absolute<R>}`:`${Absolute<R>}`:''

String to Union

字符串转联合类型

type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"

type StringToUnion<S extends string>=S extends `${infer L}${infer R}`? L | StringToUnion<R>:never

Merge对象合并

将两个类型合并为一个新类型。相同的key的情况下,第二个覆盖第一个

type A = {
  name:string;
  value:number
}

type B = {
  value:string
}

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

type Merge<F,R> = IntersectionToObj<Omit<F,keyof R>>&R

type C = Merge<A,B> // {name:string;value:string}