【不】常用小技巧
1.获取数组元素类型: type ArryType<T extends any[]> = T[number]
2.克隆数组: type CloneArr<T extends any[]> = { [k in keyof T]: T[k] }
3.推导数组产生类似as const作用: values: readonly [...T]
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{ [K in keyof T]: T[K] extends Promise<infer R> ? R : T[K] }>
4.字符串转换大写:使用内置的函数 Uppercase
5.判断是否是never:type IsNever<T> = [T] extends [never] ? true : false
注意以下写法无法判断是否是never:
❌ type Test<T> = T extends never ? true : false
但是直接使用字面量是成立的:
✅ never extends never ? true : false
6.判断是否为{} 空对象
Record<string, never>
type IsEmptyObj<T> = T extends Object ? keyof T extends never ? true : false : false
7.判断是否为联合类型
type IsUnion<T, U = T> = T extends U ? [U] extends [T]? false : true :never
8.判断是具体的字符而不是string的方法
type IsStr<T> = T extends string ? string extends T ? false : true : false
- type IsStr<T> = T extends
${infer S}
? true : false
9.判断是否为纯数字字符串
type isNumberStr = T extends `${number}` ? true : false
10.快速复制对象:type cloneObj<T extends Record<any, any>> = Omit<T, nerver>
11.去除联合类型中的undefined: type RemoveUndefined<T> = [T] extends [undefined] ? T: Exclude<T, undefined>
常见知识点
- 泛型(T)如果是never,内部直接使用 T extends xx 会直接返回never
常用工具函数
1. 设置对象所有值为never
type SetKeyNever<T, K extends keyof T> = {
[x in K]?: never;
};
2.多个属性相互互斥
/**
* 多个属性相互互斥
*/
export type JustOne<T, K extends (keyof T)[] = [], Y extends keyof T = K[number]> = NonNullable<
{
[x in Y]: Pick<T, Exclude<keyof T, Exclude<Y, x>>> & SetKeyNever<T, Exclude<Y, x>>;
}[Y]
>;
3.联合类型转交叉
/**
* 联合类型转交叉
*/
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I,
) => void
? I
: never;
4.首字母大写
/**
* 首字母大写
*/
export type FirstLetterUppercase<T extends string> = T extends `${infer F}${infer A}`
? `${Uppercase<F>}${A}`
: T;
5.删除末尾字符
/**
* 删除末尾字符
* 1.serach last
* 2.string to tuple and delete last item
*/
export type DeleteLastLetter<
T extends string,
O extends string = '',
> = T extends `${infer F}${infer R}` ? (R extends '' ? O : DeleteLastLetter<R, `${O}${F}`>) : O;
4.获取对象所有的key的完整路径
/**
* 获取对象所有的key的完整路径
* {user: {name: string, age: number}, title: string} ===> 'user.name' | 'user.age' | 'title'
*/
type PickString<T> = T extends string ? T : never;
export type GetObjFullPaths<T extends Record<string, any>> = PickString<
| keyof {
[k in keyof T as T[k] extends Record<string, any> ? never : k]: 1;
}
| keyof {
[k in keyof T as k extends string
? T[k] extends Record<string, any>
? `${k}.${GetObjFullPaths<T[k]>}`
: never
: never]: 1;
}
>;