🤍 前端开发工程师、技术日更博主、已过CET6
🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1
🕠 牛客 高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》
🍚 蓝桥云课 签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》
文章目录
-
- 一、条件类型的定义
- [二、`extends` 条件类型的高级用法](#二、
extends条件类型的高级用法) -
- (一)联合类型与条件类型
- (二)递归条件类型
- (三)分配条件类型
- [(四)使用 `infer` 关键字](#(四)使用
infer关键字)
- 三、实际应用案例
-
- [(一)实现一个类型安全的 `omit` 函数](#(一)实现一个类型安全的
omit函数) - [(二)实现一个类型安全的 `pick` 函数](#(二)实现一个类型安全的
pick函数)
- [(一)实现一个类型安全的 `omit` 函数](#(一)实现一个类型安全的
- 四、最佳实践
-
- (一)保持类型逻辑清晰
- [(二)合理使用 `infer` 关键字](#(二)合理使用
infer关键字) - (三)结合工具类型
- 五、总结
TypeScript 的条件类型是高级类型系统中的一个重要特性,它允许根据条件动态地选择类型。extends 关键字在条件类型中用于定义类型之间的关系,从而实现类型选择和类型推导。本文将详细介绍如何定义和使用 extends 条件类型,并通过实际案例展示其强大的功能。
一、条件类型的定义

(一)基本语法
条件类型的基本语法如下:
typescript
T extends U ? X : Y
T是需要检查的类型。U是参考类型。X是当T是U的子类型时选择的类型。Y是当T不是U的子类型时选择的类型。
(二)示例
以下是一个简单的条件类型示例:
typescript
type IsNumber<T> = T extends number ? true : false;
type IsNumberResult1 = IsNumber<42>; // true
type IsNumberResult2 = IsNumber<string>; // false
在这个例子中,IsNumber 是一个条件类型,它检查传入的类型 T 是否是 number 的子类型。如果是,则结果为 true;否则,结果为 false。
二、extends 条件类型的高级用法

(一)联合类型与条件类型
条件类型可以与联合类型结合使用,实现更复杂的类型选择逻辑。
示例:根据联合类型选择结果
typescript
type IsStringOrNumber<T> = T extends string | number ? true : false;
type IsStringOrNumberResult1 = IsStringOrNumber<42>; // true
type IsStringOrNumberResult2 = IsStringOrNumber<string>; // true
type IsStringOrNumberResult3 = IsStringOrNumber<boolean>; // false
在这个例子中,IsStringOrNumber 检查传入的类型 T 是否是 string 或 number 的联合类型。如果是,则结果为 true;否则,结果为 false。
(二)递归条件类型
条件类型可以递归地定义,实现更复杂的类型逻辑。
示例:深度只读类型
typescript
type DeepReadonly<T> = T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
type MyObject = {
a: number;
b: {
c: string;
d: boolean;
};
};
type DeepReadonlyMyObject = DeepReadonly<MyObject>;
// 等价于:
// type DeepReadonlyMyObject = {
// readonly a: number;
// readonly b: {
// readonly c: string;
// readonly d: boolean;
// };
// }
在这个例子中,DeepReadonly 是一个递归条件类型,它将对象的每个属性递归地转换为只读类型。
(三)分配条件类型
当条件类型与联合类型结合时,TypeScript 会自动将联合类型分配到条件类型中,实现类型分配。
示例:分配条件类型
typescript
type Flatten<T> = T extends any[] ? T[number] : T;
type FlattenResult1 = Flatten<[1, 2, 3]>; // 1 | 2 | 3
type FlattenResult2 = Flatten<string>; // string
在这个例子中,Flatten 是一个条件类型,它检查传入的类型 T 是否是数组类型。如果是,则提取数组的元素类型;否则,直接返回 T。
(四)使用 infer 关键字
infer 关键字用于在条件类型中推导类型,通常用于提取类型的一部分。
示例:提取函数返回值类型
typescript
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type GetReturnTypeResult1 = GetReturnType<() => string>; // string
type GetReturnTypeResult2 = GetReturnType<(a: number, b: string) => boolean>; // boolean
在这个例子中,GetReturnType 是一个条件类型,它使用 infer 关键字推导函数的返回值类型。
三、实际应用案例
(一)实现一个类型安全的 omit 函数
omit 函数用于从对象中移除某些属性。通过条件类型,可以实现一个类型安全的 omit 函数。
typescript
type Omit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
const result = {} as Omit<T, K>;
for (const key in obj) {
if (!keys.includes(key as K)) {
result[key as keyof Omit<T, K>] = obj[key];
}
}
return result;
}
const obj = { a: 1, b: 2, c: 3 };
const result = omit(obj, ['b', 'c']); // { a: 1 }
在这个例子中,Omit 是一个条件类型,它通过类型映射和 infer 关键字实现类型安全的属性移除。
(二)实现一个类型安全的 pick 函数
pick 函数用于从对象中提取某些属性。通过条件类型,可以实现一个类型安全的 pick 函数。
typescript
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) {
result[key] = obj[key];
}
return result;
}
const obj = { a: 1, b: 2, c: 3 };
const result = pick(obj, ['a', 'c']); // { a: 1, c: 3 }
在这个例子中,Pick 是一个条件类型,它通过类型映射实现类型安全的属性提取。
四、最佳实践
(一)保持类型逻辑清晰
条件类型可以实现复杂的类型逻辑,但过度使用可能会导致代码难以理解和维护。尽量保持类型逻辑清晰和简洁。
(二)合理使用 infer 关键字
infer 关键字是条件类型中的强大工具,但需要谨慎使用。确保推导的类型逻辑符合预期。
(三)结合工具类型
TypeScript 提供了许多内置的工具类型(如 Partial、Readonly、Omit 等),可以结合这些工具类型实现更强大的类型功能。
五、总结
extends 条件类型是 TypeScript 中一个强大且灵活的特性,允许根据条件动态地选择类型。通过条件类型,可以实现复杂的类型逻辑,如联合类型的选择、递归类型定义、分配条件类型和类型推导。合理使用条件类型可以显著提升代码的类型安全性和可维护性。希望本文能够帮助你更好地理解和使用 extends 条件类型。