TypeScript 中的 `extends` 条件类型:定义与应用

🤍 前端开发工程师、技术日更博主、已过CET6

🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1

🕠 牛客 高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》

🍚 蓝桥云课 签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》

文章目录

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 条件类型。

相关推荐
程序员Sunday31 分钟前
React 的 useEffect 怎么工作?依赖数组、清理函数和重复执行如何理解?
前端·javascript
程序员Sunday1 小时前
Vue 3 响应式原理是什么?Proxy、ref 和 reactive 有什么区别?
前端·javascript
mldong1 小时前
一个 npm 包,九页审批中心:Vue3 项目接上工作流引擎
前端·javascript·vue.js
刘发财12 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
A接拉起00712 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端
_馒头12 小时前
pixiJs游戏开发实践 - 跳棋小游戏
javascript·vue.js
三*一13 小时前
优化修整算法
前端·javascript·算法
楠楠子呀14 小时前
英语、西语、阿语客户各开一个口:多语言获客的分流方案
开发语言·javascript·ecmascript·跨境电商·whatsapp·营销工具
半个落月14 小时前
LangGraph.js 实战:从状态图到分支、循环、检查点与人工中断
javascript·人工智能·langchain
默_笙17 小时前
🧀 让大模型的答案"流"出来:NestJS AI 模块 + SSE + RxJS 实战
javascript·人工智能