盘点 20 个值得收藏点赞的 ts 代码段
ts 也有一些值得收藏点赞的代码段,帮助我们在 ts 项目中写出高级的类型,也能方便我们写出优雅的 ts 类型代码,同样的也能让我们知道一些 ts 编程的基础知识点,下面让我们一起来看看都有哪些代码段吧。
Exclude 类型
使用方式
该类型从一个类型 T 当中剔除一个类型 U。使用如下所示:
ts
type ExcludeA = string | boolean | number;
type ExcludeB = string;
type ExcludeC = Exclude<ExcludeA, ExcludeB>; // boolean | number;
type ExcludeD = string | number;
type ExcludeE = Exclude<ExcludeD, number>; // string
type ExcludeF = Exclude<ExcludeD, string | number>; // never
实现原理
ts 中使用 extends 关键字来做判断,该关键字相当于 js 的全等,因此使用这个关键字即可实现这个类型。
- 使用 extends 关键字判断类型 T 是否继承类型 U,是则返回 never,否则返回 T。
代码如下:
ts
type Exclude<T, U> = T extends U ? never : T;
Extract 类型
使用方式
该类型从一个类型 T 当中提取一个类型 U。使用如下所示:
ts
type ExtractA = string | boolean | number;
type ExtractB = string;
type ExtractC = Extract<ExtractA, ExtractB>; // string;
type ExtractD = string | number;
type ExtractE = Extract<ExtractD, number>; // number
type ExtractF = Extract<ExtractD, string | number>; // string | number
type ExtractG = Extract<ExtractA, string & number>; // never
实现原理
该类型实现原理与 Exclude 相反。
- 使用 extends 关键字判断类型 T 是否继承类型 U,是则返回 T,否则返回 never。
代码如下:
ts
type Exclude<T, U> = T extends U ? T : never;
NonNullable 类型
使用方式
该类型用于从一个类型 T 当中排出 null 和 undefined 类型。使用如下所示:
ts
type NonNullableA = null | undefined | string;
type NonNullableB = NonNullable<NonNullableA>; // string
实现原理
& 操作符在 ts 中用于取并集。
- 使用 & 将类型 T 与空对象联合,从而排除 undefined 和 null 类型。
代码如下:
ts
type NonNullable<T> = T & {};
InferArrayItem 类型
使用方式
该类型用于从一个类型数组提取数组元素的类型。使用如下所示:
ts
type InferString = InferArrayItem<string[]>; // string;
type InferStringOrNumber = InferArrayItem<string | number[]>; // string | number;
实现原理
在 ts 中可以使用 infer 关键字推断一个类型。
- 使用 infer 关键字推断数组 T 的元素类型,如果 T 继承数组 R 类型,则返回 R,否则返回 T。
代码如下:
ts
type InferArrayItem<T> = T extends (infer R)[] ? R : T;
Pick 类型
使用方式
该类型用于根据已知接口属性而从一个接口类型中提取出新的接口类型。使用如下所示:
ts
interface PickTodo {
title: string;
description: string;
completed: boolean;
}
type PickTodoPreview = Pick<PickTodo, "title" | "completed">; // { title:string;completed: boolean; }
实现原理
在 ts 中使用 in 操作符来判断一个属性是否属于接口类型的属性,使用 keyof 关键字用于提取接口类型的属性。
- 使用 keyof 获取类型 T 的属性名,组合成联合类型(类似 string | boolean),然后让 K 继承。
- 使用 in 操作符判断属性类型是否在 K 中,如果是就返回
T[K]
,也就是属性值。
代码如下:
ts
type Pick<T, K extends keyof T> = {
[R in K]: T[K];
};
Readonly 类型
使用方式
该类型用于将一个接口类型的所有属性变成只读。使用如下所示:
ts
interface ReadonlyTodo {
title: string;
description: string;
}
const todo: Readonly<ReadonlyTodo> = {
title: "Hey",
description: "foobar",
};
// todo.title = "Hello" // Error: cannot reassign a readonly property
// todo.description = "barFoo" // Error: cannot reassign a readonly property
实现原理
readonly 关键字用于将属性变成只读。
- 使用 keyof 获取类型 T 的属性名,组合成联合类型(类似 string | boolean),并设置属性名和属性值。
- 使用 readonly 关键字让属性名只读。
代码如下:
ts
type Readonly<T> = {
readonly [R in keyof T]: T[R];
};
TupleToObject 类型
使用方式
该类型用于从一个类型元组中提取元素拼接成接口类型。使用如下所示:
ts
const tuple = ["tesla", "model 3", "model X", "model Y"] as const;
type result = TupleToObject<typeof tuple>; // { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
实现原理
PropertyKey 表示接口属性。
- 使用 PropertyKey 获取数组 T 的数组项值的只读类型。
- 读取数组 T 的类型作为属性名和属性值。
代码如下:
ts
type PropertyKey = string | number | symbol;
type TupleToObject<T extends Readonly PropertyKey []> = {
[R in T[number]]: R
}
FirstArray 类型
使用方式
该类型用于从一个数组类型中提取数组第一项。使用如下所示:
ts
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type head1 = FirstArray<arr1>; // expected to be 'a'
type head2 = FirstArray<arr2>; // expected to be 3
实现原理
实现方式有很多种,可以使用 length 来判断是否是空数组,或者直接 extends 空数组,也可以使用 infer 关键字来推断数组元素。
- 数组 T 继承任意数组,如果数组 T 继承空数组,则返回 never,否则返回数组第一项。
- 数组 T 继承任意数组,如果数组 T 的长度属性为 0,则返回 never,否则返回数组第一项。
- 数组 T 继承任意数组,如果数组 T 继承任意推断的数组,则返回一项,否则返回 never。
代码如下:
ts
type FirstArray<T extends any[]> = T extends [] ? never : T[0];
// type FirstArray<T extends any []> = T['length'] extends 0 ? never : T[0];
// type FirstArray<T extends any []> = T extends [infer A,...infer rest] ? A : never;
Awaited 类型
使用方式
该类型用于从一个 Promise 类型中读取完成状态的的返回值。使用如下所示:
ts
type ExampleType = Promise<string>;
type Result = Awaited<ExampleType>; // string
实现原理
掌握 ts 中的递归,结合 extends 和 infer 关键字即可实现该类型。
- 类型 T 是否继承 null | undefined,这是一个特殊情况,需要考虑。
- 类型 T 是否继承自包含有 then 方法的对象,then 方法的第一个参数推断是存在的,其它参数用不到,所以推断为_。
- 类型 F 是否含有第一个参数,如果含有则递归的返回第一个参数,否则返回 never。
- 最后返回类型 T。
代码如下:
ts
type Awaited<T> = T extends null | undefined
? T
: T extends object & { then(onFulfilled: infer F, ...args: infer _): any }
? F extends (value: infer V, ...args: infer _) => any
? Awaited<V>
: never
: T;
If 类型
使用方式
该类型用于 ts 中的条件判断。使用如下所示:
ts
type A = If<true, "a", "b">; // 'a'
type B = If<false, "a", "b">; // 'b'
实现原理
需要注意的就是该类型的实现有 3 个参数,第一个参数用作条件判断,第二个参数用作为 true 时的返回值,第三个参数用作为 false 的返回值。
- 类型 C 继承 boolean,判断如果 C 是继承 true,则返回类型 T,否则返回类型 F。
代码如下:
ts
type If<C extends boolean, T, F> = C extends true ? T : F;
Concat 类型
使用方式
该类型用于将 2 个数组合并成一个数组。使用如下所示:
ts
type ConcatRes1 = Concat<[1], [2]>; // [1, 2]
type ConcatRes2 = Concat<[string], [number]>; // [string, number]
实现原理
展开运算符合并两个数组类型。
- 类型 T 和类型 U 都继承只读数组类型,使用展开运算符将两者合并。
代码如下:
ts
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [
...T,
...U
];
ArrayIncludes 类型
使用方式
该类型用于从一个数组类型中判断是否存在某个类型。使用如下所示:
ts
type isPillarMen1 = ArrayIncludes<
["Kars", "Esidisi", "Wamuu", "Santana"],
"Dio"
>; // `false`
type isPillarMen2 = ArrayIncludes<[string, number, boolean, null], string>; // `true`
实现原理
这有点像 js 数组的 includes 方法了。
- 类型 T 继承任意数组。
- 从类型 T 中读取值并写成一个对象,对象的属性名就是数组的每一项值,而属性值就是 true,然后根据 U 属性读取对象值。
- 判断从对象中读取出来的值是否等于 true,是就返回 true,否则返回 false。
代码如下:
ts
type ArrayIncludes<T extends readonly any[], U> = {
[R in T[number]]: true;
}[U] extends true
? true
: false;
Push 类型
使用方式
该类型用于往一个数组类型中添加类型,类似 js 数组的 push 方法。使用如下所示:
ts
type PushRes1 = Push<[1, 2], "3">; // [1, 2, '3']
type PushRes2 = Push<[number, boolean], string>; // [number, boolean, string]
实现原理
还是展开运算符的用法。
- 类型 T 继承未知的数组。
- 使用展开运算法展开 T,并且与 U 合并,返回这个合并后的数组。
代码如下:
ts
type Push<T extends unknown[], U> = [...T, U];
Unshift 类型
使用方式
该类型用于往一个数组类型中添加类型,类似 js 数组的 unshift 方法。使用如下所示:
ts
type UnshiftRes1 = Unshift<[1, 2], 0>; // [0, 1, 2]
type UnshiftRes2 = Unshift<[number, string], boolean>; // [boolean, number, string]
实现原理
还是展开运算符的用法。
- 类型 T 继承未知的数组。
- 使用展开运算法展开 T,并且与 U 合并,返回这个合并后的数组。
代码如下:
ts
type Unshift<T extends unknown[], U> = [U, ...T];
Parameters 类型
使用方式
该类型用于获取一个函数的参数类型。使用如下所示:
ts
const foo = (arg1: string, arg2: number): void => {};
type fooFunctionParamsType = Parameters<typeof foo>; // [arg1: string, arg2: number]
实现原理
infer 关键字的使用。
- 类型 T 继承任意函数。
- 使用 infer 推导函数参数,推导成功则返回,否则返回任意值。
代码如下:
ts
type Parameters<T extends (...args: any[]) => any> = T extends (
...args: infer A
) => any
? A
: never;
ReturnType 类型
使用方式
该类型用于获取一个函数的返回值。使用如下所示:
ts
const fn = (v: boolean) => {
if (v) return 1;
else return 2;
};
type a = ReturnType<typeof fn>; // should be "1 | 2"
实现原理
infer 关键字的使用。
- 类型 T 继承任意函数。
- 使用 infer 推导函数返回值,推导成功则返回,否则返回 never。
代码如下:
ts
type ReturnType<T extends (...args: any[]) => any> = T extends (
...args: any[]
) => infer A
? A
: never;
Omit 类型
使用方式
该类型用于从一个接口中删除给定的接口属性。使用如下所示:
ts
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, "description" | "title">;
const todo: TodoPreview = {
completed: false,
};
实现原理
Pick 与 Exclude 的结合使用。
- 使用 keyof 提取任意值的 key 属性,然后 K 继承它。
- 使用 Pick 挑选排除掉(Exclude)既是 T 的属性又在 K 当中存在的属性。
代码如下:
ts
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
SelectReadonly 类型
使用方式
该类型用于基于给定属性将接口中的属性设置为只读。使用如下所示:
ts
interface SelectReadonlyTodo {
title: string;
description: string;
completed: boolean;
}
const selectReadonlyTodo: SelectReadonly<
SelectReadonlyTodo,
"title" | "description"
> = {
title: "Hey",
description: "foobar",
completed: false,
};
selectReadonlyTodo.title = "Hello"; // Error: cannot reassign a readonly property
selectReadonlyTodo.description = "barFoo"; // Error: cannot reassign a readonly property
selectReadonlyTodo.completed = true; // OK
实现原理
可选只读的实现原理如下。
- 使用 keyof 提取类型 T 的属性,并使用 K 继承,K 的默认值也是 keyof T。
- 属性 R 在 K 中,所以设置为只读,排除 (Exclude)掉 K 里面的属性,keyof T 以外的属性不设置只读,两者进行合并。
代码如下:
ts
type SelectReadonly<T, K extends keyof T = keyof T> = {
readonly [R in K]: T[R];
} & {
[R in Exclude<keyof T, K>]: T[R];
};
DeepReadonly 类型
使用方式
该类型用于将接口中所有属性变成只读属性。使用如下所示:
ts
type DeepReadonlyX = {
x: {
a: 1;
b: "hi";
};
y: "hey";
};
type DeepReadonlyExpected = {
readonly x: {
readonly a: 1;
readonly b: "hi";
};
readonly y: "hey";
};
type DeepReadonlyTodo = DeepReadonly<DeepReadonlyX>; // should be same as `DeepReadonlyExpected`
实现原理
递归去设置只读关键字。
- 使用 keyof 提取类型 T 的属性,如果属性继承 never,则直接返回 T。
- 让 T 的属性只读,并递归 T 属性里面的对象。
代码如下:
ts
type DeepReadonly<T> = keyof T extends never
? T
: {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
TupleToUnionArr 类型
使用方式
该类型用于将元祖转成联合类型。使用如下所示:
ts
type TupleToUnionArr = ["1", "2", "3"];
type TupleToUnionTest = TupleToUnion<TupleToUnionArr>; // '1' | '2' | '3'
实现原理
infer 关键字的使用。
- 使用 Array 泛型,并使用 infer 推断数组项的类型,如果 T 继承这个类型,则返回推断的类型,否则返回 never。
代码如下:
ts
type TupleToUnion<T> = T extends Array<infer I> ? I : never;
以上代码段参考自网上,并总结在代码段网站中,如果觉得有用,感谢动动小手点个赞,点个收藏。
ps: 后续将会推出更复杂的类型代码段。