前言

开始提前批直通车啦~~ 本篇带来 ts 超集的各种挑战加个人解说。源码地址:传送门

Hello World

1
type HelloWorld = string;

Pick

code

1
2
3
4
5
type MyPick<T, K> = {};

type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};

解释:pick 就是获取第一个参数的类型的其中几个,写法是Pick<target,'属性1'|'属性2'>,所以要实现他,就是说第二个属性要在 T 中,extends keyof 保证 K 的所有的类型在 T 中,in 保证 P 所有的类型在 K 中。

那么这里会有一个疑问。in 和 keyof 的区别

首先 keyof 是取 interface 的联合类型,

1
2
3
4
5
6
interface UserInfo {
userName: "jojo";
psw: "123";
}
type value = keyof UserInfo;
// value= 'userName'|'psw'

in 是取联合类型,主要用于数组和对象的构建。
切记不要用于 interface 否则会报错,

1
2
3
4
5
type name = "firstName" | "lastName";
type JOJOName = {
[key in name]: string;
};
//JOJOName = {firstName:string,lastName:string}

用于实际开发,举例

1
2
3
4
5
function getValue(obj: Object, key: string) {
return obj[key];
}
const obj1 = { name: "jojo" };
const value = getValue(obj1, "name");

这样写就丧失了 ts 的优势

  1. 无法确定返回值的类型,
  2. 无法对 key 进行约束

所以我们要这样写

1
2
3
4
5
6
function getValue<T extends Object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const obj1 = { name: "jojo" };
const value = getValue(obj1, "name");
// 如果第二个参数不是obj1中的参数就会报错

Readonly

解释:需要将 T 中的参数变为只读

1
2
3
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};

Tuple to Object 元组转换为对象

解释:需要取出元组的值,并且构造成对象

1
2
3
type TupleToObject<T extends readonly any[]> = {
[P in T[number]]: P;
};

注意T[number]是可以取出元组中的值,所以不用 keyof 了

第一个元素

解释:取出 T 的第一个元素 非空判断

1
type First<T extends any[]> = T extends never[] ? never : T[0];

获取元组长度

解释:如果元组存在 length 属性则返回 length 值

1
2
3
type Length<T extends readonly any[]> = T extends { length: infer L }
? L
: never;

注意输入的类型是数组才行

Exclude

解释:排除 T 中带有 U 的类型

1
type MyExclude<T,U> = T extends U:never:T

简述 T 的类型在 U 里面就排除

Awaited

解释:实现该方法返回 promise 对象类型,要点是判断其中类型是否为 promise 在判断 promise 内的类型是否还是 promise 类型(递归)

1
2
3
4
5
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer U>
? U extends Promise<unknown>
? MyAwaited<U>
: U
: never;

If

解释:C 是 true 就返回 T 的类型,是 false 就返回 F 的类型

1
type If<C extends Boolean, T, F> = C extends true ? T : F;

Concat

解释:实现 js 的 concat 效果,合并两个类型数组。

1
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];

使用解构即可。

Includes

解释:后一个类型如果包含在前面的类型中就返回 true 否则返回 false

1
2
3
4
5
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R]
? Equal<F, U> extends true
? true
: Includes<R, U>
: false;

Push

解释:实现 js 的 push

1
type Push<T extends any[], U> = [...T, U];

Unshift

解释:实现 js 的 unshift

1
type Unshift<T extends any[], U> = [U, ...T];

Parameters

解释:获取函数参数类型以数组输出

1
2
3
4
5
type MyParameters<T extends (...args: any[]) => any> = T extends (
...args: infer K
) => any
? K
: never;

注意的点在于其实...args的类型是any[]也就是以为着结果以数组输出的使用也可以利用上,直接使用 infer 去推断他的具体类型。

获取函数返回类型

解释:获取函数的返回类型

1
type MyReturnType<T> = T extends (...args: any[]) => infer K ? K : never;

和上一题类似

Omit

解释:Pick 和 Exclude 的结合

1
2
3
4
5
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
// or
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Readonly2

解释:如果不传 K 则默认为 readonly 否则就按 K 的内容对 T 进行只读的限制。

1
2
3
4
5
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P];
} & {
[P in keyof T as P extends K ? never : P]: T[P];
};

注意的点首先是默认赋值,因为他不存在其实就是相当于用了全部的 T 的键,然后还有一点就是,他是 readonly 和 omit 的结合。在 K 中的所有值都是 readonly,不在 k 中的所有值都是 omit

Deep Readonly

解释:使用深度只读将对象的每个属性包括对象本身变成只读

1
2
3
type DeepReadonly<T> = keyof T extends never
? T
: { readonly [k in keyof T]: DeepReadonly<T[k]> };

注意其实递归设置 readonly 是比较简单的,但是细节在于递归的设置,此处如果不是对象就返回初始类型

元组转集合

解释:把元组转为集合

1
type TupleToUnion<T> = T extends (infer U)[] ? U : never;

关键在于通过 infer 推断集合的类型,以及取出元组的类型

可串联构造器

最后一个元素

解释:取出数组最后一个元素

1
type Last<T extends any[]> = T extends [...any, infer U] ? U : never;

Pop

解释:实现 pop

1
2
3
type Pop<T extends any[]> = T extends [...infer Head, infer R]
? [...Head]
: never;

Promise.all

解释:实现 PromiseAll 获取数组值的类型

1
2
3
4
5
6
7
8
type AwaitedAll<T extends any[]> = T extends []
? []
: T extends [infer F, ...infer R]
? [Awaited<F>, ...AwaitedAll<R>]
: never;
declare function PromiseAll<T extends any[]>(
value: readonly [...T]
): Promise<AwaitedAll<T>>;
  • 结合 awaited 的学习知识获取 Promise 的类型
  • 结合类型解构和递归处理 Promise 的情况
  • 注意空数组的情况
  • 因为 promise 的结果是不能改变的,所以要加 readonly
  • 解构 T 类型传入自定义的 AwaitedAll

Type Lookup

解释:从 U 中获取 type 为 T 的类型

1
type LookUp<U, T extends string> = U extends { type: T } ? U : never;

Trim Left

解释:删除字符串左边的空格

1
2
3
4
type Space = " " | "\n" | "\t";
type TrimLeft<S extends string> = S extends `${Space}${infer R}`
? TrimLeft<R>
: S;

技巧:结合模板字符串操作递归删除

Trim

解释:实现 trim 删除字符串两端的所有空格

1
2
3
4
5
6
7
8
9
10
11
12
type Space = " " | "\n" | "\t";
type Trim<S extends string> = S extends `${Space}${infer R}`
? Trim<R>
: S extends `${infer K}${Space}`
? Trim<K>
: S;
// or
type Trim<S extends string> = S extends
| `${Space}${infer R}`
| `${infer R}${Space}`
? Trim<R>
: S;

先删左边再删右边

Capitalize

解释:大写第一个字母

1
2
3
type MyCapitalize<S extends string> = S extends `${infer F}${infer R}`
? `${Uppercase<F>}${R}`
: S;

不能使用[infer F,...infer R]是因为我们解析的是单个字符串

Replace

实现 Replace 替换

1
2
3
4
5
6
7
8
9
type Replace<
S extends string,
From extends string,
To extends string
> = From extends ""
? S
: S extends `${infer F}${From}${infer R}`
? `${F}${To}${R}`
: S;