7个高效的TypeScript工具类型,你会用了吗?

news2024/11/19 13:40:46

78e9ae581e375f95ae44e0bf081a3358.jpeg

在现代Web开发中,TypeScript几乎已经成为默认技术。TypeScript本身就提供了描述代码的方法,但工具类型(Utility Types)就像给你代码加上了“超能力”!

这些工具类型能让你的代码更清晰、更简洁,同时还能减少隐藏错误的可能性。今天我们就来聊聊TypeScript中的七个高效工具类型:keyof、ReturnType、Awaited、Record、Partial、Required 和 Omit。通过实例讲解,让你轻松掌握这些强大的工具类型。

1. keyof 操作符

keyof 操作符用于获取对象的键。例如,如果你有一个表示用户的类型,并且你想创建一个只接受该用户接口键的函数。通过这种方式,你可以确保函数的参数始终是有效的。

type User = {
  id: number;
  name: string;
  email: string;
}

// 接受 User 接口的键的函数
function getUserProperty(key: keyof User): string {
  const user: User = {
    id: 1,
    name: 'Mr Smith',
    email: 'mrsmith@example.com',
  };

  // 假设每个属性都可以转换为字符串
  return String(user[key]);
}

// 有效的用法
const userName = getUserProperty('name'); // 可以,'name' 是 User 的一个键
console.log(userName);

// 错误: 类型 '"country"' 的参数不能赋给类型 'keyof User' 的参数。
// const userCountry = getUserProperty('country');

在上面的例子中,我们定义了一个 User 类型,并且创建了一个 getUserProperty 函数,该函数只接受 User 类型的键作为参数。通过使用 keyof User,我们确保了传递给函数的参数必须是 User 类型的有效键。如果你尝试传递一个不存在的键,比如 'country',TypeScript 会在编译时就抛出错误,从而帮助你避免运行时错误。

这样做的好处是可以让你的代码更健壮,并且在重构代码时可以得到更好的类型检查支持。

2. ReturnType 类型

ReturnType 类型用于获取函数的返回类型。

假设我们有一个函数,用于加载应用程序的配置。这个函数返回一个包含各种配置设置的对象。

我们希望编写另一个函数,该函数需要安全地使用这些配置数据,并依赖于配置对象的结构,而不需要手动重复定义其类型。

// 示例:定义一个返回配置对象的函数
function loadAppConfig() {
  return {
    apiUrl: 'https://api.example.com',
    retryAttempts: 5,
    debugMode: false
  };
}

// 使用 ReturnType 推断 loadAppConfig 函数返回的配置对象的类型
type AppConfig = ReturnType<typeof loadAppConfig>;

// AppConfig 现在代表我们的配置对象类型,我们可以在应用程序的其他部分安全地使用该类型
function setupApi(config: AppConfig) {
  console.log(`API URL: ${config.apiUrl}`);
  console.log(`重试次数: ${config.retryAttempts}`);
  console.log(`调试模式: ${config.debugMode ? '启用' : '禁用'}`);
}

const config = loadAppConfig();
setupApi(config);

在这个例子中,我们定义了一个 loadAppConfig 函数,该函数返回一个包含 API 配置详情的对象。通过使用 ReturnType<typeof loadAppConfig>,我们自动推断出 loadAppConfig 返回的对象类型,并将其命名为 AppConfig。这样,我们就可以在其他函数中安全地使用 AppConfig 类型,而无需手动重复定义配置对象的类型。

这种方法的好处是,在我们修改 loadAppConfig 函数的返回类型时,相关的类型定义会自动更新,减少了手动同步类型定义的工作量,并且可以在编译时进行类型检查,提高代码的健壮性和可维护性。

3. Awaited 类型

Awaited 类型用于获取等待一个 Promise 解析后的结果类型。考虑以下场景,我们向 JSONPlaceholder API 发送一个简单的 fetch 请求以获取一个特定的 todo 项目:

async function fetchTodoItem() {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  if (!response.ok) {
    throw new Error('Failed to fetch the todo item');
  }
  return await response.json();
}

在不使用 Awaited 的情况下,fetchTodoItem 的推断返回类型是 Promise<any>,因为 TypeScript 无法从 fetch 中推断响应 JSON 的结构。这时 Awaited 类型的好处就显现出来了,我们可以手动指定获取数据的预期结构:

// API 返回的 todo 项目的预期结构
type TodoItem = {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
};

// 在异步上下文中直接使用 `fetchTodoItem` 函数
async function displayTodoItem() {
  const todo: Awaited<TodoItem> = await fetchTodoItem();
  // 现在你可以在完全类型支持下使用 `todo`
  console.log(`Todo Item: ${todo.id}, Title: ${todo.title}, Completed: ${todo.completed}`);
}

displayTodoItem();

在这个例子中,我们定义了 TodoItem 类型来描述 API 返回的 todo 项目的结构。使用 Awaited<TodoItem>,我们可以确保 todo 变量在 fetchTodoItem 函数返回后具有正确的类型支持。

这种方法的真正好处在于,当 TypeScript 不能自动推断类型时,或者当你处理的类型是条件类型或类似 Promise 的类型但不完全是 Promise 时,Awaited 能让你的代码更健壮、更易维护。在这个例子中,我们通过明确指定返回数据的结构,避免了类型推断的不确定性,从而提高了代码的可靠性。

4. Record 类型

Record<Keys, Type> 是 TypeScript 中的一个工具类型,用于创建具有特定键和统一值类型的对象类型。它特别适合在你希望确保对象具有一组特定的键,并且每个键对应的值都是某种特定类型时使用。

想象一下,你在实现一个基于角色的访问控制(RBAC)系统。每个用户角色都有一组权限,决定了用户可以执行的操作。在这种情况下,Record<Keys, Type> 可以用来定义角色和权限的类型,从而确保整个应用程序的类型安全。

// 定义一组角色
type UserRole = 'admin' | 'editor' | 'viewer';

// 定义权限为结构化对象
type Permission = {
  canCreate: boolean;
  canRead: boolean;
  canUpdate: boolean;
  canDelete: boolean;
};

// 将每个用户角色映射到其权限
const rolePermissions: Record<UserRole, Permission> = {
  admin: {
    canCreate: true,
    canRead: true,
    canUpdate: true,
    canDelete: true,
  },
  editor: {
    canCreate: true,
    canRead: true,
    canUpdate: true,
    canDelete: false, // 编辑者不能删除
  },
  viewer: {
    canCreate: false,
    canRead: true,
    canUpdate: false,
    canDelete: false, // 观众只能读取
  },
};

// 检查用户角色是否有权限执行某个操作的函数
function hasPermission(role: UserRole, action: keyof Permission): boolean {
  const permissions = rolePermissions[role];
  return permissions[action];
}

// 使用 hasPermission 的示例
console.log(hasPermission('admin', 'canCreate')); // true
console.log(hasPermission('editor', 'canUpdate')); // true
console.log(hasPermission('viewer', 'canDelete')); // false
console.log(hasPermission('editor', 'canDelete')); // false

在这个例子中,我们定义了 UserRole 类型来表示不同的用户角色,并定义了 Permission 类型来表示每个角色的权限。通过使用 Record<UserRole, Permission>,我们确保每个用户角色都有一组完整的权限,并且这些权限的结构是统一的。

这种使用方法的好处是,你不能意外地漏掉某个角色的权限定义,也不能错误地定义权限的结构。通过 Record 类型,我们能够在编译时获得类型检查的支持,从而提高代码的可靠性和可维护性。这不仅能帮助你避免运行时错误,还能让你在开发过程中更有信心地修改和扩展代码。

5. Partial 类型

Partial 类型用于将对象的所有属性变为可选。举个例子,如果你有一个包含多个属性的接口,你可以使用 Partial<interface> 来创建一个所有属性都是可选的类型。

type Todo = {
  title: string;
  description: string;
}

const partialTodo: Partial<Todo> = {}; 
// partialTodo 可以拥有 Todo 的任意属性,也可以没有任何属性

实际应用场景

假设我们在开发一个待办事项(Todo)应用。在这个应用中,我们有一个 Todo 接口,用于描述待办事项的结构。然而,在某些情况下,我们可能只需要更新待办事项的一部分属性,而不是全部。这时候,Partial 类型就派上用场了。

type Todo = {
  title: string;
  description: string;
}

// 更新待办事项的函数
function updateTodo(id: number, updatedFields: Partial<Todo>) {
  // 假设我们有一个 todos 数组存储所有的待办事项
  const todos: Todo[] = [
    { title: 'Learn TypeScript', description: 'Understand basic types' },
    { title: 'Write Blog Post', description: 'Draft a new article' }
  ];

  const todo = todos.find(todo => todo.id === id);
  if (todo) {
    Object.assign(todo, updatedFields);
  }
}

// 更新一个待办事项,只修改它的 description 属性
updateTodo(1, { description: 'Understand advanced types' });

在这个例子中,我们定义了一个 updateTodo 函数,该函数接受待办事项的 id 和一个 updatedFields 对象。updatedFields 的类型是 Partial<Todo>,这意味着它可以包含 Todo 的任意属性,也可以不包含任何属性。这样我们就可以只更新待办事项的一部分属性,而不必提供完整的 Todo 对象。

使用 Partial 类型的好处是显而易见的。它使我们的代码更加灵活和可扩展,尤其是在处理需要部分更新的场景时。通过将所有属性变为可选,我们可以更方便地进行增量更新,同时也减少了代码的冗余和重复。

6. Required 类型

Required 类型与 Partial 类型相反,它用于将对象的所有属性变为必选。举个例子,如果你有一个包含多个属性的接口,你可以使用 Required<interface> 来创建一个所有属性都是必选的类型。

type Todo = {
  title: string;
  description: string;
}

// requiredTodo 必须包含 Todo 的所有属性
const wrongRequiredTodo: Required<Todo> = { title: 'Hello' }; // 错误,没有 description 属性

const correctRequiredTodo: Required<Todo> = { title: 'Hello', description: 'World' }; // 正确

实际应用场景

假设我们在开发一个待办事项(Todo)应用,在某些场景下,我们希望确保某些操作只能在待办事项的所有属性都已提供的情况下进行。这时,我们可以使用 Required 类型来确保所有属性都是必选的。

type Todo = {
  title?: string;
  description?: string;
}

// 创建一个新待办事项的函数
function createTodo(todo: Required<Todo>) {
  // 假设我们有一个 todos 数组存储所有的待办事项
  const todos: Todo[] = [];

  todos.push(todo);
}

// 尝试创建一个不完整的待办事项
const incompleteTodo = { title: 'Incomplete' };
createTodo(incompleteTodo); // 错误,description 属性是必需的

// 创建一个完整的待办事项
const completeTodo = { title: 'Complete', description: 'This is a complete todo' };
createTodo(completeTodo); // 正确

在这个例子中,我们定义了一个 createTodo 函数,该函数接受一个 Required<Todo> 类型的参数。这意味着传递给 createTodo 的对象必须包含 Todo 类型的所有属性。如果我们尝试传递一个缺少某些属性的对象,TypeScript 会在编译时抛出错误,从而帮助我们避免在运行时出现问题。

使用 Required 类型的好处在于,它可以确保我们的代码在处理需要所有属性的对象时,始终具有完整性和一致性。这不仅提高了代码的可靠性,还减少了由于缺少必要属性而导致的潜在错误。通过在适当的场景中使用 Required 类型,我们可以使代码更健壮,更易于维护。

7. Omit 类型

Omit 类型用于从对象类型中移除某些属性。例如,如果你有一个包含多个属性的接口,你可以使用 Omit<interface, "property1" | "property2"> 来创建一个不包含指定属性的类型。

type Todo = {
  title: string;
  description: string;
  createdAt: Date;
}

const todoWithoutCreatedAt: Omit<Todo, "createdAt"> = { title: "Hello", description: "World" }; 
// todoWithoutCreatedAt 不包含 createdAt 属性

实际应用场景

假设我们在开发一个待办事项(Todo)应用,我们有一个 Todo 接口,其中包含创建时间 createdAt 属性。在某些场景下,比如我们只需要展示待办事项的标题和描述,而不需要显示创建时间。此时,我们可以使用 Omit 类型来移除不必要的属性。

type Todo = {
  title: string;
  description: string;
  createdAt: Date;
}

// 移除 createdAt 属性
type TodoWithoutCreatedAt = Omit<Todo, "createdAt">;

// 模拟一个显示待办事项的函数
function displayTodo(todo: TodoWithoutCreatedAt) {
  console.log(`Title: ${todo.title}`);
  console.log(`Description: ${todo.description}`);
}

// 创建一个不包含 createdAt 属性的待办事项
const todo = { title: "Learn TypeScript", description: "Understand utility types" };
displayTodo(todo);

在这个例子中,我们定义了一个 TodoWithoutCreatedAt 类型,通过 Omit<Todo, "createdAt"> 移除了 createdAt 属性。这样,我们就可以在 displayTodo 函数中使用这个新类型,只处理 title 和 description 属性,而不需要担心 createdAt 属性的存在。

使用 Omit 类型的好处在于,它可以帮助我们创建更简洁和专注的类型,避免处理不必要的属性。这不仅使我们的代码更加清晰和易于维护,还减少了在不同场景中重复定义类型的工作量。通过在适当的场景中使用 Omit 类型,我们可以提高代码的灵活性和可读性。

结束

通过这篇文章,我们详细介绍了 TypeScript 中的七个高效工具类型:keyof、ReturnType、Awaited、Record、Partial、Required 和 Omit。这些工具类型就像给你的代码加上了“超能力”,让你的代码更清晰、更简洁,并减少了潜在的错误。

无论你是刚接触 TypeScript 的新手,还是已经有一定经验的开发者,掌握这些工具类型都能极大地提升你的编码效率和代码质量。希望这篇文章能帮助你更好地理解和使用 TypeScript,让你的开发之路更加顺畅。

如果你喜欢这篇文章,或者有任何问题和建议,欢迎在评论区留言与我互动!别忘了关注「前端达人」,获取更多前端开发的干货和技巧。期待与你一起成长,一起进步!

让我们在前端的世界里不断探索,共同进步!感谢你的阅读,我们下次再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1855642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20240623(26.0) 重要财经新闻

财经关注 ► 券商中国&#xff1a;北交所于6月21日晚间受理了3家企业的IPO申请。6月20日晚间&#xff0c;沪深交易所各受理了1家IPO申请。这也意味着&#xff0c;三大交易所IPO受理全部恢复。与此同时&#xff0c;三大交易所IPO上市委会议也已经全部重启。 ► 全球多地近期遭遇…

【C++】Cmake入门|掌握cmake的基本操作

前言&#xff1a; CMake是开源、跨平台的构建工具&#xff0c;可以让我们通过编写简单的配置文件去生成本地的Makefile&#xff0c;这个配置文件是独立于运行平台和编译器的&#xff0c;这样就不用亲自去编写Makefile了&#xff0c;而且配置文件可以直接拿到其它平台上使用&am…

vue3import的插件全局引入

webpack 的引入 npm install -D unplugin-auto-import const AutoImport require(unplugin-auto-import/webpack).default;configureWebpack: {devtool: source-map,module: {rules: [{test: /\.mjs$/,include: /node_modules/,type: javascript/auto}],}, plugins: [Aut…

建材租赁管理系统软件教程,操作简单佳易王租赁管理系统操作教程

建材租赁管理系统软件教程&#xff0c;操作简单佳易王租赁管理系统操作教程 一、软件操作教程 以下软件操作教程以&#xff0c;佳易王租赁管理系统为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 租赁登记&#xff1a; a、租赁登记可以记录日…

Codeforces Round 954 (Div. 3) A B C D

A. X Axis time limit per test: 2 second memory limit per test: 256 megabytes input: standard input output: standard output You are given three points with integer coordinates x 1 x_1 x1​, x 2 x_2 x2​, and x 3 x_3 x3​ on the X X X axis ( 1 ≤ x i ≤ …

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和…

RFID技术在汽车轮胎加工工艺中的革新应用

RFID技术在汽车轮胎加工工艺中的革新应用 物联网技术的飞速发展&#xff0c;无线射频识别&#xff08;Radio Frequency Identification&#xff0c;简称RFID&#xff09;技术因其独特的优势&#xff0c;在各行各业中展现出巨大的应用潜力。特别是在汽车制造业&#xff0c;RFID…

Java反射详细总结

什么是反射&#xff1f; 反射&#xff0c;指的是加载类的字节码到内存&#xff0c;并以编程的方法解刨出类中的各个成分&#xff08;成员变量、方法、构造器等&#xff09;。 反射获取的是类的信息&#xff0c;那么反射的第一步首先获取到类才行。由于Java的设计原则是万物皆对…

【火猫体育】欧洲杯:苏格兰VS匈牙利焦点大战

北京时间6月24日&#xff0c;欧洲杯A组苏格兰VS匈牙利的焦点大战将正式打响。这场比赛对于苏格兰队来说不容有失&#xff0c;因为球队必须战胜对手才能有希望从小组赛出线&#xff0c;晋级本届欧洲杯16强。苏格兰在欧洲杯首战&#xff0c;就被东道主德国队上了一课。德国队在比…

毕设源码直通车

看下方 文心海资源库 名片 领取源码

Python代码升级工具库之pyupgrade使用详解

概要 在Python开发过程中,随着语言版本的更新和改进,代码也需要不断地进行升级和优化,以利用新版本提供的特性和性能提升。pyupgrade 库是一个自动化工具,它能够帮助开发者将代码升级到指定的Python版本,自动应用新的语法和特性,简化了代码维护工作。本文将详细介绍 pyu…

网络物理隔离后 可以用保密U盘进行数据安全交换吗?

企业用的保密U盘通常被设计用于存储和传输敏感信息&#xff0c;以确保数据的安全和保密性。 在网络之间实现了物理隔离后&#xff0c;使用保密U盘进行数据安全交换是一种常见的做法。物理隔离确保了两个网络之间的完全分离&#xff0c;因此使用保密U盘可以作为一种安全的手段来…

MySQL面试重点-2

16. MySQL数据引擎&#xff1a; 引擎分类&#xff1a; show engines命令查看数据库支持的存储引擎。 描述一下InnoDB和MyISAM的区别&#xff1f;** InnoDB存储限制64TB&#xff0c;而MyISAM存储限制256TB&#xff1b;InnoDB支持事物&#xff0c;而MyISAM不支持&#xff1b;I…

(13)DroneCAN 适配器节点(一)

文章目录 前言 1 特点 2 固件 3 ArduPilot固件DroneCAN设置 4 DroneCAN适配器节点 前言 这些节点允许现有的 ArduPilot 支持的外围设备作为 DroneCAN 或 MSP 设备适应 CAN 总线。这也允许扩展自动驾驶仪硬件的功能。如允许 I2C 设备&#xff08;如罗盘或空速&#xff09…

maven的生命周期是什么?看这一篇就够了!

大家可能都知道maven是什么&#xff1f;Maven 是一个流行的项目管理工具&#xff0c;用于构建、发布和管理 Java 项目。那么我们在用maven将项目打包成一个jar包的时候&#xff0c;他是怎么运作的&#xff0c;mvn clean install都做了哪些小动作&#xff1f;其中的package和ins…

《web应用技术》第十二次课后作业

1.servlet基础知识 1.定义 Java Servlet 是运行在 Web 服务器或应用服务器上的程序&#xff0c;它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。 2.生命周期 init 方法被设计成只调用一次。它在第一次创建 Servlet 时被…

Python酷库之旅-第三方库openpyxl(07)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…

SVM算法-人脸识别背后技术详解

引言 支持向量机&#xff08;SVM&#xff09;是一种强大的监督学习算法&#xff0c;广泛应用于分类和回归任务中。本文将详细介绍SVM算法在人脸识别任务中的应用&#xff0c;并通过代码示例来展示其背后的技术精髓。我们将分三大部分来展开&#xff0c;本部分将重点介绍SVM算法…

数据资产与人才战略:聚焦数据人才培养与引进,构建专业团队,为企业数据资产增值提供源源不断的智力支持,确保数据资产的高效利用与持续增长

一、引言 随着信息技术的飞速发展&#xff0c;数据已成为企业最宝贵的资产之一。在数字化时代&#xff0c;数据资产的高效利用和持续增长对于企业的竞争力至关重要。而要实现这一目标&#xff0c;人才是关键。本文将围绕数据资产与人才战略展开讨论&#xff0c;重点分析数据人…

【喜报】全球第三名HCIE-openEuler在誉天诞生!

2024年6月18日&#xff0c;誉天首期HCIE-openEuler班刘同学一次性通过HCIE-openEuler实验考试&#xff0c;并且成为全球第三位HCIE-openEuler专家,刘同学也是誉天首位通过该方向的HCIE学员。 同时恭喜刘同学获得誉天欧拉HCIE专属奖学金5000元&#xff0c;让我们祝贺他&#xff…