前言
- 自从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'