一、基础学习篇
开始做类型体操前,需要学习的基本操作和常用实现模板
关键字
keyof
keyof 后跟一个对象,取所有的key组成联合类型
// 作用在对象上
type O = { foo: string, bar: string };
type OKeys = keyof O;
// "foo" | "bar"
in
in后面必须跟联合类型(Union Type),遍历联合类型的每个成员
type Keys = "foo" | "bar";
type Obj = {
[K in Keys]: any;
};
// { foo: any; bar: any; }
- 通常和
keyof配合使用
// 下面结果相同
type Person = { name: string; age: number };
type Copy = {
[K in keyof Person]: Person[K];
};
keyof + in + as可以对键名映射或过滤
type Person = { name: string; age: number; hidden: boolean };
// 过滤掉 hidden
type VisiblePerson = {
[K in keyof Person as Exclude<K, "hidden">]: Person[K];
};
extends
- 在泛型参数中使用,约束参数的范围
type Include<T extends unknown[]> = any;
- 最常用的方式! 作为条件类型判断
type IsString<T> = T extends string ? true : false;
- 分布式计算:当
extends左边是联合类型时
type ToArray = T extends any ? T[] : never;
type R = ToArray<string | number>;
// string[] | number[]
// 注意:[T] extends [U] 可以阻止分布式计算
infer
在条件类型 A extends B ? X : Y 中,”提取” 或 ”推断” 类型,可以理解为声明一个 “占位” 变量
- 推断
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R
? R
: any;
- 提取
type StringFirst<T extends string> =
T extends `${infer First}${string}`
? First
: never;
typeof
typeof + 运行时值 提取值对应的类型
const arr = ['a', 'b', 'c'] as const;
type A = typeof arr;
// readonly ["a", "b", "c"]
never
一般表示 extends 中不可达的分支
type First<T extends any[]> = T extends [] ? never : T[0];
工具/板子/基本操作
元组转联合类型
const t = ['a', 'b'] as const;
// 'a' | 'b'
type T = (typeof t)[number];
严格的 Equals
// 便捷的 Equal
type Equals<T, U> = T extends U ? U extends T ? true : false : false;
// 严格的 Equal
// https://github.com/type-challenges/type-challenges/discussions/9100#discussioncomment-6896958
type StrictEquals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
字符串相关
迭代处理字符串
type HandleString<S extends string> =
S extends `${infer First}${infer Tail}`
? HandleString<Tail>
: S;
字符串转元组
type StringToTuple<S extends string> =
S extends `${infer First}${infer Rest}`
? [First, ...StringToTuple<Rest>]
: [];
数组相关
Includes 是否包含某Type
type Includes<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
? true
: Includes<Rest, U>
: false;
ReplaceAt 替换指定索引的Type
type ReplaceAt<
T extends readonly unknown[],
Index extends number,
U
> =
Index extends keyof T
? {
[K in keyof T]: K extends `${Index}` ? U : T[K];
}
: T;
Concat 合并数组
type Concat<
T extends readonly unknown[],
U extends readonly unknown[]
> = [...T, ...U];
对象相关
移除只读属性 -readonly
type Mutable<T extends object> = {
-readonly [K in keyof T]: T[K];
}
合并交叉类型
type MergeIntersection<T> = { [K in keyof T]: T[K] };
函数
⚠️ 函数也是对象,注意判断
// true
type Flag = Function extends Record<string, any> ? true : false;
二、百道题解篇
⬇️第一阶段:难度 Easy⬇️
13-Hello World
- 热身 🤸
type HelloWorld = string;
4-Pick
- extends 限制入参 K 仅为 T 的属性
- in 遍历联合类型
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
7-Readonly 对象属性只读
- 遍历属性,全部添加属性修饰符
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
11-元组转换为对象
- 元组转为k-v相同的对象
- 使用内置类型 PropertyKey 限制元组项的类型
type TupleToObject<T extends readonly PropertyKey[]> = {
[P in T[number]]: P;
};
14-第一个元素
两种解法
- 下标取元素
- infer 取元素
type First<T extends any[]> = T extends [] ? never : T[0];
type First<T extends any[]> = T extends [infer U, ...infer reset] ? U : never;
18-获取元组长度
- 泛型约束只接受元组
readonly unknown[]
type Length<T extends readonly unknown[]> = T['length'];
43-实现 Exclude
- 这里 T、U 都是联合类型
- extends + 左侧联合类型,会触发分布式计算
type MyExclude<T, U> = T extends U ? never : T;
// 例如:
// T = 'a'|'b'|'c'|'d'
// U = 'c'|'d'
// 触发分布式计算
// 'a' extends 'c'|'d' → false → 结果 'a'
// 'b' extends 'c'|'d' → false → 结果 'b'
// 'c' extends 'c'|'d' → true → 结果 never
// 'd' extends 'c'|'d' → true → 结果 never
// 最终结果 'a'|'b'
189-Awaited
- 使用内置PromiseLike + infer 占位,获取结果
- 如果结果还是一个 Promise,需要等它完成,递归处理
type MyAwaited<T> =
T extends PromiseLike<infer U>
? U extends PromiseLike<any>
? MyAwaited<U>
: U
: never;
268-If
- C为真返回 T,C为假返回F
- 体会 extends 条件使用
type If<C extends boolean, T, F> = C extends true ? T : F;
533-Concat
合并两个数组
- 声明元组 测试用例要求
- 使用
...展开运算符合并数组
type Tuple = readonly unknown[];
type Concat<T extends Tuple, U extends Tuple> = [...T, ...U];
898-Includes
- 了解严格相等的 Hack 写法
- 递归判断每一项 equals
type Includes<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
? true
: Includes<Rest, U>
: false;
3057-Push
...展开之前数组- 新增数组项,合并为新数组
type Push<T extends unknown[], U> = [...T, U];
3060-Unshift
同上
3312-Parameters
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer U[]) => any
? [...U]
: never;
⬇️ 第二阶段 难度 Middle ⬇️
2-ReturnType 获取函数返回类型
- infer 占位提取返回类型
type MyReturnType<T extends (...args: any[]) => any>
= T extends (...args: any) => infer R
? R
: never;
3-实现 Omit
两种解法
- 方式依赖 Pick + Exclude 剔除联合类型中的值
as K extends对遍历的属性值进行过滤
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type MyOmit<T, U extends keyof T> = {
[P in keyof T as P extends U ? never : P]: T[P];
};
8-对象部分属性只读
利用已有的类型实现
- Omit 剔除不要只读的属性
- Pick 选出需要只读的属性
type MyReadonly2<T, K extends keyof T = keyof T> = Omit<T, K> & Readonly<Pick<T, K>>;
9-对象属性只读(递归)
🍀 新的技巧
- 这里 keyof 后跟对象类型才生效,如果没有可枚举的键,返回 never
- 所以
keyof T[P] extends never代表不是一个对象
type DeepReadonly<T> = {
readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>;
};
10-元素转合集(联合类型)
Tuple[number]返回元组项的联合类型
type TupleToUnion<T extends readonly unknown[]> = T[number];
12-可串联构造器
type Chainable<R = object> = {
option<K extends string, V>(
key: Exclude<K, keyof R>,
value: V
): Chainable<Omit<R, K> & Record<K, V>>;
get(): R;
}
15-最后一个元素
了解解构数组 + infer
type Last<T extends any[]> = T extends [...infer _, infer U] ? U : never;
16-排除最后一项 Pop
了解解构数组 + infer
type Pop<T extends any[]> = T extends [...infer Rest, unknown] ? Rest : T;
20-Promise.all
keyof + 元组返回元组的下标 0,1,2,3…- 结合之前的
Awaited获取每项 Promise 的返回值
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<{
[K in keyof T]: Awaited<T[K]>;
}>;
62-查找类型 LookUp
- 利用 extends + 左侧联合类型,分布式计算
type LookUp<U, T extends string> = U extends { type: T } ? U : never;
106-去除左侧空白
- 声明空白联合类型
infer U代表未处理的 Rest 字符串,递归计算去除
type Space = ' ' | '\n' | '\t';
type TrimLeft<S extends string> = S extends `${Space}${infer U}` ? TrimLeft<U> : S;
108-去除两端空白字符 Trim
同上,写一个 TrimRight 结合起来
type Space = ' ' | '\n' | '\t';
type TrimLeft<S extends string> = S extends `${Space}${infer Rest}` ? TrimLeft<Rest> : S;
type TrimRight<S extends string> = S extends `${infer Rest}${Space}` ? TrimRight<Rest> : S;
type Trim<S extends string> = TrimRight<TrimLeft<S>>;
110-Capitalize
首字母大写,两种思路
- 使用 Uppercase
- 写一个字母表代替 Uppercase
type MyCapitalize<S extends string> = S extends `${infer X}${infer Tail}` ? `${Uppercase<X>}${Tail}` : S;
116-Replace
模式匹配
type Replace<S extends string, From extends string, To extends string> =
From extends ''
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${R}`
: S;
119-ReplaceAll
模式匹配 + 递归
type ReplaceAll<S extends string, From extends string, To extends string> =
From extends ''
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${ReplaceAll<R, From, To>}`
: S;
191-追加参数
了解参数类型声明的写法
type AppendArgument<Fn extends (...args: any) => any, T> =
(...args: [...Parameters<Fn>, T]) => ReturnType<Fn>;
298-Length of String
- 字符串遍历转数组
- 返回数组[”length”]
type StringToArray<S extends string, Arr extends string[] = []> =
S extends `${infer First}${infer Tail}`
? StringToArray<Tail, [...Arr, First]>
: Arr;
type LengthOfString<S extends string> = StringToArray<S>['length'];
459-Flatten
- 递归数组每一项
- 判断是否是数组,是则继续递归,否则递归剩余部分
type Flatten<T extends any[]> =
T extends [infer First, ...infer Tail]
? First extends unknown[]
? [...Flatten<First>, ...Flatten<Tail>]
: [First, ...Flatten<Tail>]
: T;
527-Append to object
- 旧key用旧值,新key用新值
type AppendToObject<T, K extends PropertyKey, V> = {
[P in keyof T | K]: P extends keyof T ? T[P] : V;
};
529-Absolute
返回一个正数字符串
- “-” 开头,返回剩余部分
type Absolute<T extends number | string | bigint> =
`${T}` extends `${infer First}${infer Tail}`
? First extends '-'
? `${Tail}`
: `${T}`
: T;
531-String to Union
两种方式
- 递归构造联合类型
- 转数组,返回数组[number]
type StringToUnion<T extends string> =
T extends `${infer First}${infer Tail}`
? First | StringToUnion<Tail>
: never;
599-Merge
两种方式
- 重新合并交叉类型
- 手动构造对象,优先取
S[K]
type Merge<F, S, P = Omit<F, keyof S> & S> = {
[K in keyof P]: P[K];
};
type Merge<F, S> = {
[K in keyof (F & S)]: K extends keyof S ? S[K] : K extends keyof F ? F[K] : never;
};
612-KebabCase
驼峰字符串转连字符
- 拆分单个字符,递归处理
- 头部额外的条件分支,直接变小写,非头部变 “-小写”
- 没有使用内置 Uppercase 因为
F extends Uppercase<F>非字母时始终为True
type UppercaseAlphabet = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
type KebabCase<S extends string, IsHead = true> =
S extends `${infer F}${infer Tail}`
? F extends UppercaseAlphabet
? IsHead extends true
? `${Lowercase<F>}${KebabCase<Tail, false>}`
: `-${Lowercase<F>}${KebabCase<Tail, false>}`
: `${F}${KebabCase<Tail, false>}`
: S;
645-Diff
获取两个接口类型的属性差值
- 互相剔除
keyof - 联合起来
- 合并交叉类型,返回结果
type MergeIntersection<T> = { [K in keyof T]: T[K] };
type Diff<A, B> = MergeIntersection<Omit<A, keyof B> & Omit<B, keyof A>>;
949-AnyOf
数组中有任一项为真,返回 true
- 出现了分布式计算
- 分布式计算每项都为假,返回 false
type Falsy =
| null
| undefined
| false
| 0
| ''
| []
| Record<string, never>
type AnyOf<T extends readonly unknown[]> = T[number] extends Falsy ? false : true;
1042-IsNever
- 使用
[T] extends [never]避免分布式计算
type IsNever<T> = [T] extends [never] ? true : false;
1130-ReplaceKeys
- U 是接口联合类型、T 是字符串联合类型、Y 是要替换的新接口
- 遍历
keyof U,如果属性是T和Y的key则替换,否则使用原来的值
type ReplaceKeys<U, T extends string, Y> = {
[K in keyof U]:
K extends T
? K extends keyof Y
? Y[K]
: never
: U[K]
};
1367-Remove Index Signature
移除掉形如 [k: string],仅保留字面量的 Key
- 仅保留字面量的key,通用类型的key一律返回 never
- 利用 as 过滤 key
type RemoveIndexSignature<T> = {
[K in keyof T as
number extends K
? never
: string extends K
? never
: symbol extends K
? never
: K
]: T[K];
};
1978-Percentage Parser
拆分一个百分数字符串,如 -80% => ["-", "80", "%"]
type ParseSign<T> = T extends `${infer X extends '+'|'-'}${string}` ? X : '';
type ParseNumber<T> = T extends `${ParseSign<T>}${infer X}${ParsePercent<T>}` ? X : '';
type ParsePercent<T> = T extends `${string}${infer X extends '%'}` ? X : '';
type PercentageParser<T extends string> = [ParseSign<T>, ParseNumber<T>, ParsePercent<T>]
2070-Drop Char
从字符串中剔除指定字符
- 逐个拆分字符,和 C 比较
- 递归拼接
type DropChar<S extends string, C extends string> =
S extends `${infer First}${infer Tail}`
? `${First extends C ? '' : First}${DropChar<Tail, C>}`
: '';
2595-PickByType
Pick 指定 Type 的 key
- 利用 as 过滤属性的类型
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: U;
};
2688-StartsWith
Easy!
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false;
2688-EndsWith
Easy!
type EndsWith<T extends string, U extends string> = T extends `${string}${U}` ? true : false;
2757-PartialByKeys
指定联合类型 keys 可选 ?
- 属于 K 的,可选
- 之前的 keyof 剔除 K 后,不可选
type Merge<T> = { [K in keyof T]: T[K] };
type PartialByKeys<T, K extends keyof T = any> =
Merge<
{
[P in keyof T as P extends K ? P : never]?: T[P];
} & {
[P in Exclude<keyof T, K>]: T[P];
}
>;
2759-RequiredByKeys
指定联合类型key,必选
type Merge<T> = { [K in keyof T]: T[K] };
type RequiredByKeys<T, K extends keyof T = keyof T> = Merge<T & Required<Pick<T, K>>>;
2793-Mutable
全部变为可变,去除 readonly
- 使用
-readonly可以去除
type Mutable<T extends Record<any, any>> = {
-readonly [K in keyof T]: T[K];
};
2852-OmitByType
2595-PickByType 的反例
type OmitByType<T, U> = {
[K in keyof T as T[K] extends U ? never : K]: T[K];
};
2946-ObjectEntries
返回类似 Object.entries
- 分布式计算,遍历联合类型
K = keyof T - 返回具体的类型值要注意:
undefined返回、可选的返回变为必选
type ObjectEntries<T, K extends keyof T = keyof T> =
K extends PropertyKey
? [K, T[K] extends undefined ? undefined : Required<T>[K]]
: never;
3062-Shift
Easy!
type Shift<T extends unknown[]> = T extends [unknown, ...infer Rest] ? Rest : T;
3188-Tuple to Nested Object
示例:TupleToNestedObject<['a', 'b'], number> // { a: { b: number }}
- 递归处理对象就好
- 注意要写
[K in First]
type TupleToNestedObject<T extends unknown[], U> =
T extends [infer First extends PropertyKey, ...infer Rest]
? { [K in First]: TupleToNestedObject<Rest, U> }
: U;
3192-Reverse
- 不断拆最后一项,放到第一项
- 递归处理反转
type Reverse<T extends unknown[]> =
T extends [...infer Rest, infer U]
? [U, ...Reverse<Rest>]
: T;
3196-Flip Arguments
反转函数参数的类型声明
两种解法
// 1. 利用内置对象
type FlipArguments2<Fn extends (...args: any) => any> = (...args: Reverse<Parameters<Fn>>) => ReturnType<Fn>;
// 2. 手动 infer + Reverse 翻转
type FlipArguments<Fn extends (...args: any) => any> =
Fn extends (...args: infer A) => infer R
? (...args: Reverse<A>) => R
: never;
3243-FlattenDepth
指定深度数组扁平化
type FlattenDepth<
T extends unknown[],
Depth extends number = 1,
Count extends unknown[] = [],
> =
Count['length'] extends Depth
? T
: T extends [infer F, ...infer Rest]
// 处理一项,是数组则递归
? F extends unknown[]
// 处理一项,层次+1
? [...FlattenDepth<F, Depth, [...Count, unknown]>, ...FlattenDepth<Rest, Depth, Count>]
: [F, ...FlattenDepth<Rest, Depth, Count>]
: T;
3326-BEM style string
type BEM<B extends string, E extends string[],M extends string[]> =
`${B}${E extends [] ? '' : `__${E[number]}`}${M extends [] ? '' : `--${M[number]}`}`;
3376-InorderTraversal
中序遍历,左根右
interface TreeNode {
val: number
left: TreeNode | null
right: TreeNode | null
}
type InorderTraversal<T extends TreeNode | null> =
T extends TreeNode
? [...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
: [];
4179-Flip
接口 K-V 转 V-K
type Flip<T extends Record<any, any>> = {
[K in keyof T as `${T[K]}`]: K;
};
4425-Greater Than
比较两个数的大小(仅解决了非大数的场景)
- 转换为比较元组A和元组B
- 每次遍历砍掉一项,谁先为空谁就短
type BuildTuple<N extends number, T extends unknown[] = []> =
T['length'] extends N
? T
: BuildTuple<N, [...T, unknown]>;
type GreaterTupleLengthThan<A extends unknown[], B extends unknown[]> =
A extends [infer _, ...infer RestA]
? B extends [infer _, ...infer RestB]
? GreaterTupleLengthThan<RestA, RestB>
: A
: B;
type GreaterThan<A extends number, B extends number> = GreaterTupleLengthThan<BuildTuple<A>, BuildTuple<B>>;
4471-Zip
接受两个元组,返回 [[A[0], B[0]], [A[1], B[1]]]
- 两个元组每一项都拆分处理
- 递归
type Zip<A extends unknown[], B extends unknown[]> =
A extends [infer A0, ...infer TRest]
? B extends [infer B0, ...infer URest]
? [[A0, B0], ...Zip<TRest, URest>]
: []
: [];
4484-IsTuple
判断是元组,而不是数组或never
- 要避免T是联合类型,阻止分布式计算
- ‼️元组的 length 属性是字面量,不是 number 类型
type IsTuple<T> =
[T] extends [never]
? false
: T extends readonly any[]
? number extends T['length']
? false
: true
: false;
4499-Chunk
n 个一组拆分数组
- 声明临时变量存每一段chunk、声明变量R存最后结果
- 递归遍历,直到 T 每一项都处理完成
type Chunk<
T extends unknown[],
N extends number = 1,
Temp extends unknown[] = [],
Result extends unknown[] = [],
> =
T extends [infer Head, ...infer Tail]
? Temp['length'] extends N
? Chunk<Tail, N, [Head], [...Result, Temp]>
: Chunk<Tail, N, [...Temp, Head], Result>
: Temp['length'] extends 0
? Result
: [...Result, [...Temp]];
4518-Fill
数组指定范围内,全部填充指定值
type Fill<
T extends unknown[],
NewType,
Start extends number = 0,
End extends number = T['length'],
Count extends unknown[] = [],
Flag extends boolean = Count['length'] extends Start ? true : false,
> =
Count['length'] extends End
? T
// 一定在 End 之前
: T extends [infer First, ...infer Rest]
? Flag extends false
// 没到 Start,用 Flag 默认值判断是否到了
? [First, ...Fill<Rest, NewType, Start, End, [...Count, unknown]>]
// 刚好到 Start,Flag 要保持 True
: [NewType, ...Fill<Rest, NewType, Start, End, [...Count, unknown], true>]
: T;
4803-Trim Right
同 TrimLeft
type Space = ' ' | '\n' | '\t';
type TrimRight<S extends string> = S extends `${infer Rest}${Space}` ? TrimRight<Rest>: S;
5117-去除数组指定元素
U 是一个数组,转联合类型用于判断
type ToUnion<T> = T extends unknown[] ? T[number] : T;
type Without<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? F extends ToUnion<U>
? [...Without<Rest, U>]
: [F, ...Without<Rest, U>]
: T;
5140-Trunc
数字截尾取整
type Trunc<S extends number | string> =
`${S}` extends `${infer L}.${string}`
? L extends '' | '-' | '+'
? `${L}0`
: L
: `${S}`;
5153-IndexOf
返回指定值所在数组的索引
- 利用 Count[’length’] 计数
- 注意严格 Equals
type IndexOf<T extends unknown[], U, Count extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? StrictEqual<F, U> extends true
? Count['length']
: IndexOf<Rest, U, [...Count, unknown]>
: -1;
5310-Join
数组转字符串,按照 ”,” 分隔
type Join<T extends string[], U extends string | number = ','> =
T extends [infer F extends string, ...infer Rest extends string[]]
// 最后一项不需要 , 结尾
? Rest['length'] extends 0
? F
: `${F}${U}${Join<Rest, U>}`
: '';
5317-LastIndexOf
同 IndexOf,返回最后一个出现的
type LastIndexOf<
T extends unknown[],
U,
Count extends unknown[] = [],
LatestIndex extends number = -1
> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
// 仅相等时,更新最新的index
? LastIndexOf<Rest, U, [...Count, unknown], Count['length']>
: LastIndexOf<Rest, U, [...Count, unknown], LatestIndex>
: LatestIndex;
5360-Unique
数组去重
type Includes<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
? true
: Includes<Rest, U>
: false;
type Unique<T extends unknown[], R extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? Includes<R, F> extends true
? Unique<Rest, R>
: Unique<Rest, [...R, F]>
: R;
5821-MapTypes
示例
type StringToNumber = { mapFrom: string; mapTo: number;}
MapTypes<{foo: string}, StringToNumber> // { foo: number; }
type MapTypes<T extends object, U extends { mapFrom: any; mapTo: any }> = {
[K in keyof T]:
// 值类型匹配 mapForm
T[K] extends U['mapFrom']
// 这里 U 用例可能是个联合类型,需要匹配联合中具体的一项
? U extends { mapFrom: T[K] }
? U['mapTo']
: never
: T[K];
};
7544-Construct Tuple
构造一个指定长度的元组
type ConstructTuple<L extends number, R extends unknown[] = []> =
R['length'] extends L
? R
: ConstructTuple<L, [...R, unknown]>;
8640-Number Range
2,6 返回 [2, 3, 4, 5, 6] 数组
type NumberRange<
L extends number,
H extends number,
Count extends unknown[] = [],
R extends number[] = [],
Flag extends boolean = Count['length'] extends L ? true : false,
> =
Count['length'] extends H
? R[number] | H
: Flag extends true
? NumberRange<L, H, [...Count, unknown], [...R, Count['length']], true>
: NumberRange<L, H, [...Count, unknown], R>
;
9142-CheckRepeatedChars
检查数组是否有重复字符
- 逐个拆分遍历字符
- 判断当前字符有没有在后面出现过
Tail extends{Head}${string}`` - 递归判断
type CheckRepeatedChars<T extends string> =
T extends `${infer F}${infer Tail}`
? Tail extends `${string}${F}${string}`
? true
: CheckRepeatedChars<Tail>
: false;
9286-FirstUniqueCharIndex
查找第一个唯一字符的索引
type Includes<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? Equals<F, U> extends true
? true
: Includes<Rest, U>
: false;
type FirstUniqueCharIndex<
T extends string,
Count extends unknown[] = [],
Prev extends unknown[] = [],
> =
T extends `${infer Head}${infer Tail}`
? Includes<Prev, Head> extends true
// 1. 看当前字符前面有没有出现过
? FirstUniqueCharIndex<Tail, [...Count, unknown], [...Prev, Head]>
// 2. 看后面有没有出现过
: Tail extends `${string}${Head}${string}`
? FirstUniqueCharIndex<Tail, [...Count, unknown], [...Prev, Head]>
: Count['length']
: -1;
9616-Parse URL Params
解析 URL 中的 :params
type ParseUrlParams<T> =
// 砍掉 ":" 及之前的
T extends `${string}:${infer R}`
// 砍掉 "/" 之后的,就是参数
? R extends `${infer Head}/${infer Tail}`
// 递归联合
? Head | ParseUrlParams<Tail>
: R
: never;
9896-获取数组的中间元素
- 不断砍两边的项
T['length'] extends 0 | 1 | 2时,返回结果
type GetMiddleElement<T extends unknown[]> =
T['length'] extends 0 | 1 | 2
? T
: T extends [unknown, ...infer Rest, unknown]
? GetMiddleElement<Rest>
: never;
9898-找出目标数组中只出现过一次的元素
思路类似FirstUniqueCharIndex,只不过要完全递归完,结果收集起来
type Includes<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
? true
: Includes<Rest, U>
: false;
type FindEles<T extends unknown[], Prev extends unknown[] = [], R extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? Includes<Prev, F> extends false
? Includes<Rest, F> extends false
? FindEles<Rest, [...Prev, F], [...R, F]>
: FindEles<Rest, [...Prev, F], R>
: FindEles<Rest, [...Prev, F], R>
: R;
9989-统计数组中的元素个数
- 数组扁平化
- 写一个单一获取个数的函数
- 组合起来问题的拆解
type Flatten<T extends unknown[], R extends unknown[] = []> =
T extends [infer First, ...infer Tail]
? First extends unknown[]
? Flatten<[...First, ...Tail], R>
: Flatten<Tail, [...R, First]>
: R;
type GetCount<T extends unknown[], E, Count extends unknown[] = []> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, E> extends true
? GetCount<Rest, E, [...Count, unknown]>
: GetCount<Rest, E, Count>
: Count['length'];
/**
* 1. 扁平化 flatten
* 2. 筛选合法的key extends PropertyKey
* 3. 每个 key 统计个数
*/
type CountElementNumberToObject<
T extends any[],
U extends any[] = Flatten<T>
> = {
[K in U[number]]: GetCount<U, K>;
};
10969-Integer
判断是一个整数
- 是一个字面量类型
number extends T判断排除 - 通过
“.”分隔,判断点后是否都为0
type IsZero<T extends string> =
T extends `${infer F}${infer Rest}`
? F extends '0'
? IsZero<Rest>
: false
: never;
type Integer<T extends number> =
number extends T
? never
: `${T}` extends `${infer _}.${infer Right}`
? Right extends string
? IsZero<Right> extends true
? T
: never
: T
: T;
16259-将类型为字面类型(标签类型)的属性,转换为基本类型
type ToPrimitive<T> =
T extends object
? T extends Function
? Function
: { [K in keyof T]: ToPrimitive<T[K]> }
// 原始类型或包装类型
: T extends { valueOf: () => infer U } ? U : T;
17973-DeepMutable
- 去除
-readonly - 函数或者非对象,递归处理
type DeepMutable<T extends Record<string, any>> = {
-readonly [K in keyof T]:
T[K] extends Record<string, any>
? T[K] extends Function
? T[K]
: DeepMutable<T[K]>
: T[K];
};
18242-All
数组中所有元素都等于 U
- 每项递归
- 如果存在一项不相等,结束递归返回 false
type All<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? StrictEquals<F, U> extends true
? All<Rest, U>
: false
: true;
18220-Filter
接受一个联合类型U,仅保留符合U的值
- 递归处理一下就好
type Filter<T extends unknown[], U> =
T extends [infer F, ...infer Rest]
? F extends U
? [F, ...Filter<Rest, U>]
: [...Filter<Rest, U>]
: T;
21104-FindAll
给定字符串T,字符串U,返回索引数组,满足以U开头的下标
- 判断是否以某字符串开头
Count["length"]对下标计数、R存下标结果、U_0代表当前拆解处理的字符- 逐个处理,如果首个字符相同,判断剩余部分是否相同。如果相同则存下标
- 递归处理每个字符
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false;
type StringFirst<T extends string> = T extends `${infer First}${string}` ? First : never;
type FindAll<
T extends string,
U extends string,
Count extends unknown[] = [],
R extends number[] = [],
U_0 extends string = StringFirst<U>,
> =
T extends `${infer F}${infer Rest}`
? F extends U_0
? StartsWith<T, U> extends true
// 存下标
? FindAll<Rest, U, [...Count, unknown], [...R, Count['length']]>
// 透传,处理下一个字符
: FindAll<Rest, U, [...Count, unknown], R>
: FindAll<Rest, U, [...Count, unknown], R>
: R;
21106-组合键类型 Combination key type
示例:前一个key可以组合后面所有的key,后面的key不能组前面的key
type ModifierKeys = ['cmd', 'ctrl', 'opt', 'fn']
type CaseTypeOne = 'cmd ctrl' | 'cmd opt' | 'cmd fn' | 'ctrl opt' | 'ctrl fn' | 'opt fn'
- 实现一个组合单个 key 的方法
ComboOne(联合起来,递归拼接) - 递归调用
ComboOne联合起来
type ComboOne<K1 extends string, KList extends string[]> =
KList extends [infer K2 extends string, ...infer KRest extends string[]]
? `${K1} ${K2}` | ComboOne<K1, KRest>
: never;
type Combs<T extends string[]> =
T extends [infer K1 extends string, ...infer KRest extends string[]]
? ComboOne<K1, KRest> | Combs<KRest>
: never;
25170-Replace First
替换第一个出现的 X,变成 Y
type ReplaceFirst<T extends unknown[], X, Y> =
T extends [infer F, ...infer Rest]
? F extends X
? [Y, ...Rest]
: [F, ...ReplaceFirst<Rest, X, Y>]
: [];
27862-CartesianProduct
输入两个联合类型,输出联合类型的各种组合
示例
CartesianProduct<1 | 2, 'a' | 'b'>
// [1, 'a'] | [2, 'a'] | [1, 'b'] | [2, 'b']
- 利用分布式计算处理
- 如上,我们需要构造:
1|2 extends [”a”]、1|2 extends [”b”] - 实现一个值联合类型转[值]数组联合
UnionToTupleUnion
// 值联合 转 [值]联合
// UnionToTupleUnion<2 | 3 | 4> -> [2] | [3] | [4]
type UnionToTupleUnion<T> = T extends T ? [T] : never;
type CartesianProduct<T, U> = T extends T ? [T, ...UnionToTupleUnion<U>] : never;
27958-CheckRepeatedTuple
查找元组中有无重复的项
type CheckRepeatedTuple<T extends unknown[]> =
T extends [infer F, ...infer Rest]
? Includes<Rest, F> extends true
? true
: CheckRepeatedTuple<Rest>
: false;
28333-Public Type
去除 “_” 开头的 key
type PublicType<T extends object> = {
[K in keyof T as
K extends string
? `${K}` extends `_${string}`
? never
: K
: K
]: T[K];
};
29650-ExtractToObject
拆对象的一层key,合并到上层
type ExtractToObject<T extends Record<string, any>, U extends keyof T> = {
[K in (keyof Omit<T, U> | keyof T[U])]: K extends keyof T ? T[K] : T[U][K];
};
30301-IsOdd
判断是奇数
- 排除小数
- 判断是否以 13579 结尾
type IsOdd<T extends number> =
`${T}` extends `${number}.${number}` | `${number}e${number}`
? false
: `${T}` extends `${number | ''}${1 | 3 | 5 | 7 | 9}`
? true
: false;
29785-Deep Omit
接受的 Path 可能以 “.” 分隔
- 遍历对象key,如果key是path一部分,则继续递归,否则返回原本值
- 递归到头,使用Omit剔除最内层对象
type DeepOmit<T, Path extends string> =
Path extends `${infer K}.${infer Rest}`
? {
[_K in keyof T]: _K extends K ? DeepOmit<T[_K], Rest> : T[_K]
}
: Omit<T, Path>;
34007-Compare Array Length
比较数组的长度,谁先被砍完谁短
type CompareArrayLength<T extends unknown[], U extends unknown[]> =
T extends [infer _, ...infer TRest,]
? U extends [infer _, ...infer URest]
? CompareArrayLength<TRest, URest>
: 1
: U extends []
? 0
: -1;
35191-Trace
获取二维数组斜对角的值联合类型
type Trace<
T extends unknown[][],
Count extends unknown[] = [],
R extends any = T[0][0],
> =
Count['length'] extends T['length']
? R
: Trace<T, [...Count, unknown], R | T[Count['length']][Count['length']]>;
35252-IsAlphabet
判断是否为26英文字母
- 一个比较妙的做法,如果非英文字母,使用内置大小写转换结果一致,英文字母则不一致
- 如果是英文
"A" extends "a"如果非英文"-" extends "-"
type IsAlphabet<S extends string> = Uppercase<S> extends Lowercase<S> ? false : true;
35991-MyUppercase
实现内置 Uppercase
interface LetterDict {
a: 'A';
b: 'B';
c: 'C';
d: 'D';
e: 'E';
f: 'F';
g: 'G';
h: 'H';
i: 'I';
j: 'J';
k: 'K';
l: 'L';
m: 'M';
n: 'N';
o: 'O';
p: 'P';
q: 'Q';
r: 'R';
s: 'S';
t: 'T';
u: 'U';
v: 'V';
w: 'W';
x: 'X';
y: 'Y';
z: 'Z';
};
type MyUppercase<T extends string> =
T extends `${infer F}${infer Tail}`
? F extends keyof LetterDict
? `${LetterDict[F]}${MyUppercase<Tail>}`
: `${F}${MyUppercase<Tail>}`
: T;