- Published on
TypeScript 类型体操
- Authors

- 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'
Capitalize 是 ts 内置的,还内置了三个,总共是四个,如下
- 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
说了这么多,来稍微介绍一下 infer,infer可以理解成let(js),也就是声明了一个东西
我们拿上面一个例子来举例
type NotEmpty<Arr extends unknownp> = Arr extends [...infer X,infer Last] ? true
可以看到这里infer X和 infer Last,如果不写infer,TS会去找,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>}`
: ''
}