前言

  • 自从ts出了字面量后,玩法就一下子多了,很多类型可能盲写写不出来,但是开发时候复制粘贴来引用写库还是很有用的。
  • 当然拿来做做面试题也可以。

类型整理

CapitalizeString首字母大写

type CapitalizeString<T> = T extends `${infer R}${infer K}` ? `${Uppercase<R>}${K}` : T
type CapitalizeString<T extends string | number> = T extends string? Capitalize<T> : T;
type a1 = CapitalizeString<'handler'> // Handler
type a2 = CapitalizeString<'parent'> // Parent
type a3 = CapitalizeString<233> // 233

FirstChar 获取字符串字面量中的第一个字符

type FirstChar<T> = T extends `${infer R}${infer _S}` ? R : never
type A = FirstChar<'BFE'> // 'B'
type B = FirstChar<'dev'> // 'd'
type C = FirstChar<''> // never

LastChar获取字符串字面量中的最后一个字符

type LastChar<T extends string, A extends string[] = []> = T extends `${infer P}${infer Q}`
  ? LastChar<Q, [...A, P]>
  : A extends [...infer _L, infer R]
  ? R
  : never
type A = LastChar<'BFE'> // 'E'
type B = LastChar<'dev'> // 'v'
type C = LastChar<'D'> // never

StringToTuple字符串转换为元组类型

type StringToTuple<T, A extends string[] = []> =T extends `${infer P}${infer Q}`
  ? StringToTuple<Q, [...A, P]>
  :  A
type A = StringToTuple<'BFE.dev'> // ['B', 'F', 'E', '.', 'd', 'e','v']
type B = StringToTuple<''> // []

TupleToString将字符串类型的元素转换为字符串字面量类型

type TupleToString<T, R extends string = ''> = T extends [infer A, ...infer B]
  ? A extends `${infer AA}`
    ? TupleToString<B, `${R}${AA}`>
    : ''
  : R
type A = TupleToString<['a', 'b', 'c']> // 'abc'
type B = TupleToString<[]> // ''
type C = TupleToString<['a']> // 'a'

RepeatString<T,C>复制字符T为字符串类型,长度为C

//利用数组length记录递归次数
type RepeatString<
  T extends string,
  C extends number,
  S extends string = '',
  A extends any[] = []
> = A['length'] extends C ? S : RepeatString<T, C, `${T}${S}`, [1, ...A]>
type A = RepeatString<'a', 3> // 'aaa'
type B = RepeatString<'a', 0> // ''

SplitString将字符串字面量类型按照指定字符,分割为元组。无法分割则返回原字符串字面量

type SplitString<
	T,
	Separator extends string,
	A extends any[] = []
> = T extends ""
	? A
	: T extends `${infer L}${Separator}${infer R}`
	? SplitString<R, Separator, [...A, L]>
	: [...A, T];
type A1 = SplitString<"handle-open-flag", "-">; // ["handle", "open", "flag"]
type A2 = SplitString<"open-flag", "-">; // ["open", "flag"]
type A3 = SplitString<"handle.open.flag", ".">; // ["handle", "open", "flag"]
type A4 = SplitString<"open.flag", ".">; // ["open", "flag"]
type A5 = SplitString<"open.flag", "-">; // ["open.flag"]

LengthOfString计算字符串字面量类型的长度

type LengthOfString<T extends string, TT extends any[] = []> = T extends ""
	? TT["length"]
	: T extends `${infer L}${infer R}`
	? LengthOfString<R, [...TT, L]>
	: never;
type A = LengthOfString<"BFE.dev">; // 7
type B = LengthOfString<"">; // 0

KebabCase驼峰命名转横杠命名

type KebabCaseIterator<T extends string, TT extends any[] = []> = T extends ""
	? TT
	: T extends `${infer L}${infer R}`
	? KebabCaseIterator<
			R,
			[...TT, L extends Uppercase<L> ? `-${Lowercase<L>}` : L]
	  >
	: never;
type JoinString<T> = T extends [infer L, ...infer R]
	? L extends string
		? `${L}${JoinString<R>}`
		: never
	: "";
type RemoveFstSeparator<T> = T extends `-${infer R}` ? R : T;
type KebabCase<T extends string> = RemoveFstSeparator<
	JoinString<KebabCaseIterator<T>>
>;
type a1 = KebabCase<"HandleOpenFlag">; // handle-open-flag
type a2 = KebabCase<"OpenFlag">; // open-flag

CamelCase横杠命名转化为驼峰命名

type SplitString<
	T,
	Separator extends string,
	A extends any[] = []
> = T extends ""
	? A
	: T extends `${infer L}${Separator}${infer R}`
	? SplitString<R, Separator, [...A, L]>
	: [...A, T];
type CamelCaseIterator<T, Ret extends string = ""> = T extends [
	infer L,
	...infer R
]
	? L extends string
		? CamelCaseIterator<R, `${Ret}${Capitalize<L>}`>
		: never
	: Ret;
type CamelCase<T> = CamelCaseIterator<SplitString<T, "-">>;
type a1 = CamelCase<"handle-open-flag">; // HandleOpenFlag
type a2 = CamelCase<"open-flag">; // OpenFlag

JoinString字符串拼接

type JoinString<
	T extends string,
	K extends string,
	Separator extends string
> = T extends ""
	? K extends ""
		? never
		: K
	: K extends ""
	? T
	: `${T}${Separator}${K}`;
type js1 = JoinString<"top", "title", ".">; // top.title
type js2 = JoinString<"", "title", ".">; // title
type js3 = JoinString<"top", "", ".">; // top
type js4 = JoinString<"", "", ".">; // never

ObjectAccessPaths得到对象中的值访问字符串

type ObjectAccessPaths<
	T extends Record<string, any>,
	Prev extends string = "",
	K = keyof T
> = K extends keyof T
	? K extends string
		? T[K] extends Record<string, any>
			? ObjectAccessPaths<T[K], JoinString<Prev, K, ".">>
			: JoinString<Prev, K, ".">
		: never
	: never;
// {
//     home: {
//         topBar: {
//             title: '顶部标题',
//             welcome: '欢迎登录'
//         },
//         bottomBar: {
//             notes: 'XXX备案,归XXX所有',
//         },
//     },
//     login: {
//         username: '用户名',
//         password: '密码'
//     }
// }
// 得到联合类型:
/*
home.topBar.title | home.topBar.welcome | home.bottomBar.notes | login.username | login.password
*/
function createI18n<Schema>(
	schema: Schema
): (path: ObjectAccessPaths<Schema>) =>  string {
	return [{ schema }] as any;
}
// i18n函数的参数类型为:home.topBar.title | home.topBar.welcome | home.bottomBar.notes | login.username | login.password
const i18n = createI18n({
	home: {
		topBar: {
			title: "顶部标题",
			welcome: "欢迎登录",
		},
		bottomBar: {
			notes: "XXX备案,归XXX所有",
		},
	},
	login: {
		username: "用户名",
		password: "密码",
	},
});

i18n("home.topBar.title"); // correct
i18n("home.topBar.welcome"); // correct
i18n("home.bottomBar.notes"); // correct
// i18n('home.login.abc')              // error,不存在的属性
// i18n('home.topBar')                 // error,没有到最后一个属性

LengthOfTuple计算元组类型的长度

type LengthOfTuple<T extends any[]> = T["length"]
type A = LengthOfTuple<['B', 'F', 'E']> // 3
type B = LengthOfTuple<[]> // 0

FirstItem得到元组类型中的第一个元素

type FirstItem<T> = T extends [infer L, ...infer _R] ? L : never;
type A = FirstItem<[string, number, boolean]>; // string
type B = FirstItem<["B", "F", "E"]>; // '

LastItem得到元组类型中的最后一个元素

type LastItem<T> = T extends [...infer _L, infer R] ? R : never
type A = LastItem<[string, number, boolean]> // boolean
type B = LastItem<['B', 'F', 'E']> // 'E'
type C = LastItem<[]> // never

Shift移除元组类型中的第一个类型

type Shift<T> = T extends [infer _L, ...infer R] ? R : T

type A = Shift<[1, 2, 3]> // [2,3]
type B = Shift<[1]> // []
type C = Shift<[]> // []

Push在元组类型中添加新的类型

type Push<T extends any[], V> = [...T, V]
type A = Push<[1,2,3], 4> // [1, 2, 3, 4]
type B = Push<[1], 2> // [1, 2]
type C = Push<[], string> // [string]

ReverseTuple反转元组

type ReverseTuple<T, TT extends any[] = []> = T extends [infer L, ...infer R]
	? ReverseTuple<R, [L, ...TT]>
	: TT;

type A = ReverseTuple<[string, number, boolean]>; // [boolean, number, string]
type B = ReverseTuple<[1, 2, 3, 4, 5]>; // [5,4,3,2,1]
type C = ReverseTuple<[]>; // []

Flat拍平元组

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

type A = Flat<[1, 2, 3]>; // [1,2,3]
type B = Flat<[1, [2, 3], [4, [5, [6]]]]>; // [1,2,3,4,5,6]
type C = Flat<[]>; // []
type D = Flat<[1]>; // [1]

Repeat<T,C>复制类型T为C个元素的元组类型

type Repeat<T, K, TT extends any[] = []> = TT["length"] extends K
	? TT
	: Repeat<T, K, [...TT, T]>;

type A = Repeat<number, 3>; // [number, number, number]
type B = Repeat<string, 2>; // [string, string]
type C = Repeat<1, 1>; // [1, 1]
type D = Repeat<0, 0>; // []

Filter<T,A>保留元组类型T中的A类型

export type Filter<T extends any[], K, TT extends any[] = []> = T extends [
	infer L,
	...infer R
]
	? Filter<R, K, [L] extends [K] ? [...TT, L] : TT>
	: TT;

type A = Filter<[1, "BFE", 2, true, "dev"], number>; // [1, 2]
type B = Filter<[1, "BFE", 2, true, "dev"], string>; // ['BFE', 'dev']
type C = Filter<[1, "BFE", 2, any, "dev"], string>; // ['BFE', any, 'dev']

FindIndex<T,E>找出E类型在元组类型T中的下标

type Equal<T, K> = [T] extends [K]
	? [K] extends [T]
		? keyof T extends keyof K
			? keyof K extends keyof T
				? true
				: false
			: false
		: false
	: false;

type FindIndex<T extends any[], K> = T extends [...infer left, infer last]
	? Equal<K, last> extends true
		? left["length"]
		: FindIndex<left, K>
	: never;
type index1 = FindIndex<[1, 111, 2, 3, 5], 111>;
type index2 = FindIndex<[1, 2, string, 3, 5], string>;
type index3 = FindIndex<[1, 2, 3, string, 5], string>;
type index4 = FindIndex<[1, 2, 3, 5, string], string>;

TupleToEnum元组类型转换为枚举类型

type Equal<T, K> = [T] extends [K]
		? [K] extends [T]
			? keyof T extends keyof K
				? keyof K extends keyof T
					? true
					: false
				: false
			: false
		: false;

	type FindIndex<T extends any[], K> = T extends [...infer left, infer last]
		? Equal<K, last> extends true
			? left["length"]
			: FindIndex<left, K>
		: never;
	type TupleToEnum<T extends string[], K = false> = {  [k in T[number]]: K extends true ? FindIndex<T, k> : k }
	// 默认情况下,枚举对象中的值就是元素中某个类型的字面量类型
	type a1 = TupleToEnum<["MacOS", "Windows", "Linux"]>
	// 如果传递了第二个参数为true,则枚举对象中值的类型就是元素类型中某个元素在元组中的index索引,也就是数字字面量类型
	type a2 = TupleToEnum<["MacOS", "Windows", "Linux"], true>

Slice截取元组中的部分元素

type Slice<
	A extends any[],
	S extends number,
	E extends number = A["length"],
	Prev extends any[] = [],
	SA extends any[] = [],
	EA extends any[] = []
> = A extends [infer First, ...infer Rest]
	? SA["length"] extends S
		? EA["length"] extends E
			? [...Prev, First]
			: Slice<Rest, S, E, [...Prev, First], SA, [...EA, null]>
		: Slice<Rest, S, E, Prev, [...SA, null], [...EA, null]>
	: Prev;

type A1 = Slice<[any, never, 1, "2", true, boolean], 0, 2>; // [any,never,1]                    从第0个位置开始,保留到第2个位置的元素类型
type A2 = Slice<[any, never, 1, "2", true, boolean], 1, 3>; // [never,1,'2']                    从第1个位置开始,保留到第3个位置的元素类型
type A3 = Slice<[any, never, 1, "2", true, boolean], 1, 2>; // [never,1]                        从第1个位置开始,保留到第2个位置的元素类型
type A4 = Slice<[any, never, 1, "2", true, boolean], 2>; // [1,'2',true,boolean]             从第2个位置开始,保留后面所有元素类型
type A5 = Slice<[any], 2>; // []                               从第2个位置开始,保留后面所有元素类型
type A6 = Slice<[], 0>;

Splice删除并且替换部分元素

type Splice<
	A extends any[],
	S extends number,
	L extends number,
	R extends any[] = [],
	Prev extends any[] = [],
	SA extends any[] = [],
	DA extends any[] = []
> = A extends [infer First, ...infer Rest]
	? SA["length"] extends S
		? DA["length"] extends L
			? [...Prev, ...R, ...A]
			: Splice<Rest, S, L, R, Prev, SA, [...DA, null]>
		: Splice<Rest, S, L, R, [...Prev, First], [...SA, null], DA>
	: Prev;

type A1 = Splice<[string, number, boolean, null, undefined, never], 0, 2>; // [boolean,null,undefined,never]               从第0开始删除,删除2个元素
type A2 = Splice<[string, number, boolean, null, undefined, never], 1, 3>; // [string,undefined,never]                     从第1开始删除,删除3个元素
type A3 = Splice<
	[string, number, boolean, null, undefined, never],
	1,
	2,
	[1, 2, 3]
>; // [string,1,2,3,null,undefined,never]          从第1开始删除,删除2个元素,替换为另外三个元素1,2,3

OptionalKeys获取对象类型中的可选属性的联合类型

type ExcludeUndefined<T> = { [K in keyof T]: Exclude<T[K], undefined> };

type OptionalKeys<T, K = keyof T> = K extends keyof T ? (undefined extends ExcludeUndefined<T>[K] ? K : never) : never
type a11 = OptionalKeys<{
	foo: number | undefined;
	bar?: string;
	flag: boolean;
}>; // bar
type a22 = OptionalKeys<{ foo: number; bar?: string }>; // bar
type a33 = OptionalKeys<{ foo: number; flag: boolean }>; // never
type a44 = OptionalKeys<{ foo?: number; flag?: boolean }>; // foo|flag
type a55 = OptionalKeys<{}>; // never

PickOptional保留一个对象中的可选属性类型

type ExcludeUndefined<T>= {[K in keyof T]:Exclude<T[K],undefined>}
type OptionalKeys<T, K = keyof T> = K extends keyof T ? (undefined extends ExcludeUndefined<T>[K] ? K : never) : never
type PickOptional<T> = Pick<T,OptionalKeys<T>>;
type a1 = PickOptional<{ foo: number | undefined, bar?: string, flag: boolean }>        // {bar?:string}
type a2 = PickOptional<{ foo: number, bar?: string }>                                   // {bar?:string}
type a3 = PickOptional<{ foo: number, flag: boolean }>                                  // {}
type a4 = PickOptional<{ foo?: number, flag?: boolean }>                                // {foo?:number,flag?:boolean}
type a5 = PickOptional<{}>

RequiredKeys获取对象类型中的必须属性的联合类型

type ExcludeUndefined<T> = {[K in keyof T]:Exclude<T[K],undefined>}
type RequiredKeys<T, K = keyof T> = K extends keyof T ? (undefined extends ExcludeUndefined<T>[K] ? never : K) : never
type a1 = RequiredKeys<{ foo: number | undefined, bar?: string, flag: boolean }>        // foo|flag
type a2 = RequiredKeys<{ foo: number, bar?: string }>                                   // foo
type a3 = RequiredKeys<{ foo: number, flag: boolean }>                                  // foo|flag
type a4 = RequiredKeys<{ foo?: number, flag?: boolean }>                                // never
type a5 = RequiredKeys<{}>                                                              // never

PickRequired保留一个对象中的必须属性

type ExcludeUndefined<T>= {[K in keyof T]:Exclude<T[K],undefined>}
type OptionalKeys<T, K = keyof T> = K extends keyof T ? (undefined extends ExcludeUndefined<T>[K] ?never :  K) : never
type PickRequired<T> = Pick<T,OptionalKeys<T>>;
type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }>        // {foo:number|undefined,flag:boolean}
type a2 = PickRequired<{ foo: number, bar?: string }>                                   // {foo:number}
type a3 = PickRequired<{ foo: number, flag: boolean }>                                  // {foo:number,flag:boolean}
type a4 = PickRequired<{ foo?: number, flag?: boolean }>                                // {}
type a5 = PickRequired<{}>

Merge合并两个对象类型T以及K,如果属性重复,则以K中属性类型为准

type Merge<T, K> = { [k in Exclude<keyof T, keyof K>]: T[k] } & K
type obj1 = {
    el: string,
    age: number
}
type obj2 = {
    el: HTMLElement,
    flag: boolean
}
type obj3 = Merge<obj1, obj2>   // {el:HtmlElement,age:number,flag:boolean}
const a = {...{} as obj3}
console.log(a.el.scrollTop, a.age.toFixed(0), a.flag.valueOf())
// console.log(a.el.charAt(0))     // error

IsNever判断是否为never类型

type IsNever<T> = [T] extends [never] ? true : false

type A = IsNever<never> // true
type B = IsNever<string> // false
type C = IsNever<undefined> // false
type D = IsNever<any> // false

IsEmptyType判断是否为没有属性的对象类型

type IsEmptyType<T> = number extends T
    ? keyof T extends never
        ? T extends {}
            ? true
            : false
        : false
    : false
type A = IsEmptyType<string> // false
type B = IsEmptyType<{ a: 3 }> // false
type C = IsEmptyType<{}> // true
type D = IsEmptyType<any> // false
type E = IsEmptyType<object> // false
type F = IsEmptyType<Object> // false
type G = IsEmptyType<unknown> // false

IsAny判断是否为any类型

// 使用 [T] 避免传入的是联合类型导致类型分布
// unknown 只能赋值给 any或者unknown
// any可以赋值给string,但是unknown不可以赋值给string
type IsAny<T> = [unknown] extends [T] ? ([T] extends [string] ? true : false) : false

type A = IsAny<string> // false
type B = IsAny<any> // true
type C = IsAny<unknown> // false
type D = IsAny<never> // false

UnionToBooleanProps有且只有一个属性

// 实现思路是把联合类型变成单值boolean交叉其他属性联合
type UnionToBooleanProps<T extends string, TT extends string = T> =
    T extends any ?
        { [k in Exclude<TT, T>]?: void } & { [k in T]: boolean; }
        : never
type MessageStringType = "info" | "success" | "warning" | "error";
type OneMessageTypes = UnionToBooleanProps<MessageStringType>
type Props = OneMessageTypes & { id: string; }

function Component(props: Props) {
    return <></>
}
const a = <Component id="abc" info/>           //correct
const b = <Component id="abc" success/>        //correct
const c = <Component id="abc"/>                //wrong
const d = <Component id="abc" info success/>   //wrong

UnionToIntersection将联合类型转换为交叉类型

// 思路 利用逆变做
type UnionToIntersection<T> = (T extends any ? (t: T) => void : never) extends (
	r: infer R
) => any
	? R
	: never;
type A = UnionToIntersection<{ a: string } | { b: string } | { c: string }>; // {a: string} & {b: string} & {c: string}

UnionPop取出来联合类型中的任意一个类型

// ((x: 1) => void) & ((x: 2) => void) & ((x: 3) => void) 利用重载函数只取最后一个的特性
type UnionPop<U> = (
	(U extends any ? (k: (x: U) => void) => void : never) extends (
		k: infer I
	) => void
		? I
		: never
) extends (a: infer A) => void
	? A
	: never;

type a = 1 | 2 | 3;
type b = UnionPop<a>; // 3

UnionToTuple联合类型转换为元组类型

type UnionPop<U> = (
	(U extends any ? (k: (x: U) => void) => void : never) extends (
		k: infer I
	) => void
		? I
		: never
) extends (a: infer A) => void
	? A
	: never;
type UnionToTuple<T, TT = T, R extends any[] = []> = [T] extends [R[number]]
	? R
	: UnionToTuple<T, Exclude<TT, UnionPop<TT>>, [UnionPop<TT>, ...R]>;

type a = UnionToTuple<1 | 2 | 3>; // [1,2,3]
type b = UnionToTuple<1 | string | boolean>; // [1,string,false, true]

IF类似三元表达式

type IsAny<T> = [unknown] extends [T] ? ([T] extends [string] ? true : false) : false
type isTrue<T> = IsAny<T> extends true ? false : [T] extends [true] ? [true] extends [T] ? true : false : false;
type IF<T, F1, F2> = isTrue<T> extends true ? F1 : F2

type a = IF<1 extends 1 | 2 ? true : false, 'aaa', 'bbb'>           // aaa
type b = IF<3 extends 1 | 2 ? true : false, 'aaa', 'bbb'>           // bbb

UnwrapPromise获取推导的promise类型

type UnwrapPromise<T extends Promise<any>> = T extends Promise<infer R> ? R : never

type A = UnwrapPromise<Promise<string>> // string
type B = UnwrapPromise<Promise<null>> // null

ValueTypeFromMap获取map里的所有类型

type ValueTypeFromMap<T> = { [k in keyof T]: T[k] }[keyof T]
type a = ValueTypeFromMap<{ name: string, age: number, flag?: boolean }>
// string|number|boolean

PickAndRequired挑选键并变为必选

type PickAndRequired<T, K extends string> = {
    [M in K]: M extends keyof T ? Exclude<T[M], undefined> : never
}
type xx = PickAndRequired<{ name?: string, age?: number, flag?: boolean, id: object }, 'name' | 'flag' | 'id'>
// {  name: string;flag: boolean;id: object;}

LargerThan判断大于

type LargerThan<
	T extends number,
	K extends number,
	TT extends any[] = [],
	KK extends any[] = []
> = T extends TT["length"]
	? K extends KK["length"]
		? false /*相等*/
		: false /*小于*/
	: K extends KK["length"]
	? true /*大于*/
	: LargerThan<T, K, [...TT, ""], [...KK, ""]>;
type A = LargerThan<0, 1>; // false
type B = LargerThan<1, 0>; // true
type C = LargerThan<10, 9>; // true
type D = LargerThan<1, 1>; // false

SmallerThan判断小于

type SmallerThan<
	T extends number,
	K extends number,
	TT extends any[] = [],
	KK extends any[] = []
> = T extends TT["length"]
	? K extends KK["length"]
		? false /*相等*/
		: true /*大于*/
	: K extends KK["length"]
	? false /*小于*/
	: SmallerThan<T, K, [...TT, ""], [...KK, ""]>;

type A = SmallerThan<0, 1>; // true
type B = SmallerThan<1, 0>; // false
type C = SmallerThan<10, 9>; // false

Add数字相加

type Add<
	A extends number,
	B extends number,
	AA extends any[] = [],
	BB extends any[] = []
> = A extends AA["length"]
	? B extends BB["length"]
		? [...AA, ...BB]["length"]
		: Add<A, B, AA, [...BB, ""]>
	: Add<A, B, [...AA, ""], BB>;

type A = Add<1, 2>; // 3
type B = Add<0, 0>; // 0

ToNumber字符串类型转数字

type ToNumber<T, K extends any[] = []> = T extends `${K["length"]}`
	? K["length"]
	: ToNumber<T, [...K, ""]>;

type A = ToNumber<"1">; // 1
type B = ToNumber<"40">; // 40
type C = ToNumber<"0">; // 0

Trim去除字符串前后空格

type Trim<T> = T extends ` ${infer R}`
	? Trim<R>
	: T extends `${infer L} `
	? Trim<L>
	: T;

type A = Trim<"    BFE.dev">; // 'BFE'
type B = Trim<" BFE. dev  ">; // 'BFE. dev'
type C = Trim<"  BFE .   dev  ">; // 'BFE .   dev'