Published on

TypeScript 类型体操

Authors
  • avatar
    Name
    叫我小N就好啦
    GitHub
    @MinorN

类型体操

体操基本原理

//	比如想实现if(a<= b) true else false
type A = 1
type B = 1 | 2

type Result = A extends B ? true : false

如何判断一个空的元组呢?

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

如何判断一个非空元组?

// 判断非空元祖
// 当然,把上边的true false 调换过来也可以实现

type A = [1]
type NotEmpty<Arr extends unknownp> = Arr extends [...infer X, infer Last] ? true : false
type Result = NotEmpty<A>

...infer X是什么??,infer Last又是什么?

换个方式来看一下

type A = [1]
type NotEmpty<Arr extends unknownp> = Arr extends [...unknown[], unknown[]] ? true : false
type Result = NotEmpty<A>

// [...unknown [],unknown []] 可以看成把前面那项展开,后面固定有一项,那么是不是就代表里面有内容,那么就可以判断元组是否非空

infer是什么,之后再细说

再来看看一个例子,我们如何翻转一个元组

type A = ['wo', 'shi', 'MinorN']
type Reverse<Arr extends unknown[]> = Arr extends [...infer Rest, infer Last]
  ? [Last, ...Reverse<Rest>]
  : Arr
type Result = Reverse<A>

先判断当前元组是不是一个空的元祖,每次把Last放到第一个,递归调用即可

再来看一个

type A = ["wo","shi","MinorN"]
type Result1 = A extends [infet First, ...infet Rest] ? First : never	// 'wo'
type Result2 = A extends [infet First, ...infer Rest] ? Rest : never	// ["shi","MinorN"]

元组体操

变长元组

type A = [1]
type B = [...A, 2]
type C = [3, 4]
type D = [...A, ...C]

取元组中的一个

type D = [1, 2, 3, 4] // 注意!!!是类型!1 2 3 4 都是类型!!我们在学习TS
// 取最后一个
type Last<T> = T extends [...infer Rest, infer L] ? L : never
type E = Last<D> // 4
// 取除了最后一个的所有
type NotLast<T> = T extends [...infer Rest, infer L] ? Rest : never
type E = Last<D> // [1,2,3]

字符串体操

type A = 'minorN'
type B = Capitalize<A> // 'MinorN'

Capitalizets 内置的,还内置了三个,总共是四个,如下

  • Uppercase:全大写
  • Lowercase:全小写
  • Capitalize:首字母大写
  • Uncapitalize:首字母小写
// 模版字符串
type A = 'hello'
type B = 'MinorN'
type Result = `${A} ${B}` // 'hello MinorN'

如何获取字符串的第一个?

type A = 'hello MinorN'
type First<T extends string> = T extends `${infer F}${string}` ? F : never
type Result = First<A> // 'h'

如何获取字符串的最后一个?

type A = 'hello MinorN'
type First<T extends string> = T extends `${string}${infer F}` ? F : never
type Result = First<A> // 'ello MinorN'
// 不是我们想要的

经过如上操作发现并不是我们想要的,那么该如何做呢?

我们可以获取元组的最后一个,那么我们是不是可以吧字符串转换成元组,然后直接取元组最后一个就可以了

// 获取元组最后一个
type Last<T extends unknown[]> = T extends [...infet Rest, infet L] ? L : never

// 字符串变成元组
type StringToTuple<T extends string> = T extends `${infer F}${infer R}` ? [F, ...StringToTuple<R>] : []
type A = 'hello MinorN'
type LastOfString<T extends string> = Last<StringToTuple<T>>
type Result = LastOfString<A>	// 'N'

字符串转联合类型

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

infer

说了这么多,来稍微介绍一下 inferinfer可以理解成let(js),也就是声明了一个东西

我们拿上面一个例子来举例

type NotEmpty<Arr extends unknownp> = Arr extends [...infer X,infer Last] ? true

可以看到这里infer Xinfer Last,如果不写inferTS会去找,X,Last 是什么?结果找不到报错,加上这个infer,也就是告诉TS,这是我声明的一个类型

官方地址 :infer

类型体操实践

先放类型体操实践的地址:type-challenge

实现TS内置的Pick

描述:从类型 T 中选择出属性 K,构造成一个新的类型

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

实现TS内置的 Parameters 类型

// Eg
const foo = (arg1: string, arg2: number): void => {}

type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]
type MyParameters<T extends (...args:any[])=>any> = {
    T extends (...args:infer X)=>unknown ? X : never
}
// T 继承 一个函数,函数的入参是 any ,出参也是 any

实现 Awaited

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

// Eg
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string
type MyAwaited<T extends Promise<any>> = {
    T extends Promise<infer X> ?
    	X extends Promise<any> ? MyAwaited<X> : X
    : T
}
// 多一层是为了防止Promise里面嵌套Promise

实现Zip(4471)

描述:In This Challenge, You should implement a type Zip<T, U>, T and U must be Tuple

// Eg
type exp = Zip<[1, 2], [true, false]> // expected to be [[1, true], [2, false]]
type MyZip<T extends any[],K extends any[]> = {
    A extends [infer AFirst,...infer ARest]
    	?B extends [infer BFirst,...infer BRest]
    		?[[AFirst,BFirst],...Zip<ARest,BRest>]
			:[]
		:[nixuy]
}

实现IsTuple(4484)

描述:Implement a type IsTuple, which takes an input type T and returns whether T is tuple type.

也就是判断是否是元组

// Eg
type case1 = IsTuple<[number]> // true
type case2 = IsTuple<readonly [number]> // true
type case3 = IsTuple<number[]> // false
type IsTuple<T> = {
    [T] extends [never]
    ? fasle
    : T extends readonly any[]
    	? number extends T['length']
    		? false
    		: true
    	: T extends {length: number}
    		? false
    		: true
}

实现Join(5310)

描述:Implement the type version of Array.join, Join<T, U> takes an Array T, string or number U and returns the Array T with U stitching up.

type Res = Join<['a', 'p', 'p', 'l', 'e'], '-'> // expected to be 'a-p-p-l-e'
type Res1 = Join<['Hello', 'World'], ' '> // expected to be 'Hello World'
type Res2 = Join<['2', '2', '2'], 1> // expected to be '21212'
type Res3 = Join<['o'], 'u'> // expected to be 'o'
type Join<T extends string[],K extends string | number> = {
    T extends [infer First extends string, ...infer Rest extends string[]]
    	? Rest['length'] extends 0
    		? First
    		: `${First}${K}${Join<Rest,K>}`
    	: ''
}