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 是当 TU 的子类型时选择的类型。
  • 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 是否是 stringnumber 的联合类型。如果是,则结果为 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 提供了许多内置的工具类型(如 PartialReadonlyOmit 等),可以结合这些工具类型实现更强大的类型功能。

五、总结

extends 条件类型是 TypeScript 中一个强大且灵活的特性,允许根据条件动态地选择类型。通过条件类型,可以实现复杂的类型逻辑,如联合类型的选择、递归类型定义、分配条件类型和类型推导。合理使用条件类型可以显著提升代码的类型安全性和可维护性。希望本文能够帮助你更好地理解和使用 extends 条件类型。

相关推荐
像我这样帅的人丶你还10 小时前
前端监控体系与实践:从错误上报到内存与 GC 观测
前端·javascript·架构
a11177610 小时前
高斯泼溅 (Gaussian Splatting) 的 Three.js 实现
开发语言·javascript·ecmascript
代码北人生10 小时前
agent时代,我们都低估了这个 23k Star 的 Claude Code Skills 项目!
javascript
成都渲染101云渲染666610 小时前
云渲染全面支持3dsMax 2027,高效渲染体验升级
开发语言·前端·javascript
zs宝来了10 小时前
微前端架构:qiankun 沙箱隔离与样式冲突
前端·javascript·框架
M ? A10 小时前
Vue 的 scoped 样式穿透 React 不支持?用 VuReact 编译就行
前端·javascript·vue.js·react.js·面试·开源·vureact
zs宝来了10 小时前
Vue 3 Composition API:响应式系统与依赖追踪
前端·javascript·框架
村上小树11 小时前
非常简单地学习一下slate.js的原理
前端·javascript
web打印社区11 小时前
[特殊字符] 开源好物:web-print-pdf,让 Web 打印像调用接口一样简单!
前端·javascript·vue.js·electron
大家的林语冰12 小时前
TS 登顶第一语言;JS 最新 Temporal 时间减屎;Node 爆发反 AI 运动;CSS 将支持图片亮暗切换《前端周刊》
前端·javascript·css