”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 掌握 TypeScript:了解扩展的力量

掌握 TypeScript:了解扩展的力量

发布于2024-11-06
浏览:415

Mastering TypeScript: Understanding the Power of extends

TypeScript 中的 extends 关键字就像一把瑞士军刀。它用于多种上下文,包括继承、泛型和条件类型。了解如何有效地使用扩展可以生成更健壮、可重用和类型安全的代码。

使用扩展进行继承

extends 的主要用途之一是继承,允许您创建基于现有接口或类的新接口或类。

interface User {
  firstName: string;
  lastName: string;
  email: string;
}

interface StaffUser extends User {
  roles: string[];
  department: string;
}

const regularUser: User = {
  firstName: "John",
  lastName: "Doe",
  email: "[email protected]"
};

const staffMember: StaffUser = {
  firstName: "Jane",
  lastName: "Smith",
  email: "[email protected]",
  roles: ["Manager", "Developer"],
  department: "Engineering"
};

在此示例中,StaffUser 扩展了 User,继承了其所有属性并添加了新属性。这使我们能够基于更通用的类型创建更具体的类型。

类继承

extends关键字也用于类继承:

class Animal {
  constructor(public name: string) {}

  makeSound(): void {
    console.log("Some generic animal sound");
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name);
  }

  makeSound(): void {
    console.log("Woof! Woof!");
  }

  fetch(): void {
    console.log(`${this.name} is fetching the ball!`);
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
myDog.makeSound(); // Output: Woof! Woof!
myDog.fetch(); // Output: Buddy is fetching the ball!

这里,Dog 扩展了 Animal,继承了它的属性和方法,同时还添加了它自己的属性和方法。

泛型中的类型约束

extends 关键字在使用泛型时至关重要,它允许我们限制可与泛型函数或类一起使用的类型。

interface Printable {
  print(): void;
}

function printObject(obj: T) {
  obj.print();
}

class Book implements Printable {
  print() {
    console.log("Printing a book.");
  }
}

class Magazine implements Printable {
  print() {
    console.log("Printing a magazine.");
  }
}

const myBook = new Book();
const myMagazine = new Magazine();

printObject(myBook);      // Output: Printing a book.
printObject(myMagazine);  // Output: Printing a magazine.
// printObject(42);       // Error, number doesn't have a 'print' method
  1. 接口Printable:这里,我们定义了一个名为Printable的接口。该接口声明了任何实现它的类都必须遵守的契约。契约规定任何实现 Printable 的类必须提供一个名为 print 的方法,该方法不带参数并返回 void
  2. function printObject(obj: T):这是一个名为 printObject 的通用函数。它采用名为 obj 的单个参数,该参数为 T 类型。类型参数 T 被限制为扩展(实现)Printable 接口的类型,可以用作此函数的参数。
  3. Book 类实现 Printable,Magazine 类实现 Printable:这里,我们定义两个类,Book 和 Magazine,它们都实现 Printable 接口。这意味着这些类必须根据 Printable 接口的约定提供打印方法。
  4. const myBook = new Book(); const myMagazine = new Magazine();:我们创建 Book 和 Magazine 类的实例。
  5. 打印对象(myBook); and printObject(myMagazine);:我们用 Book 和 Magazine 的实例调用 printObject 函数。由于 Book 和 Magazine 类都实现了 Printable 接口,因此它们满足 T extends Printable 类型参数的约束。在函数内部,调用相应类的 print 方法,从而产生预期的输出。
  6. // printObject(42);:如果我们尝试使用未实现 Printable 接口的类型(例如数字 42)调用 printObject,TypeScript 将引发错误。这是因为不满足类型约束,因为 number 没有 Printable 接口所要求的打印方法。

总之,函数 printObject(obj: T) 上下文中的 extends 关键字用于确保用作参数的类型 T 遵守 Printable 接口定义的约定。这确保只有具有 print 方法的类型才能与 printObject 函数一起使用,从而强制执行函数使用的特定行为和契约。

条件类型

T extends U ? X : Y
  • T 是正在检查的类型
  • U 是 T 正在检查的条件类型。
  • 如果 T 扩展(可分配给)U,则 X 是条件类型求值的类型
  • Y 是 T 不扩展 U 时条件类型求值的类型
type ExtractNumber = T extends number ? T : never;

type NumberOrNever = ExtractNumber; // number
type StringOrNever = ExtractNumber; // never

此处,ExtractNumber 类型采用类型参数 T。条件类型检查 T 是否扩展了数字类型。如果是,则类型解析为 T(这是数字类型)。如果不存在,则类型解析为 never。

具有联合类型的 extends 关键字

现在,让我们考虑表达式 A |乙| C 扩展了 A。乍一看这似乎违反直觉,但在 TypeScript 中,这个条件实际上是错误的。原因如下:

  1. 在 TypeScript 中,当您在左侧使用带有联合类型的 extends 时,相当于询问:“这个联合中的每个可能的类型都可以分配给右侧的类型吗?”
  2. 换句话说,A |乙| C 扩展 A 正在询问:“可以将 A 分配给 A,并且可以将 B 分配给 A,并且可以将 C 分配给 A?”
  3. 虽然 A 当然可以分配给 A,但 B 和 C 可能无法分配给 A(除非它们是 A 的子类型),因此总体结果为 false。
type Fruit = "apple" | "banana" | "cherry";
type CitrusFruit = "lemon" | "orange";

type IsCitrus = T extends CitrusFruit ? true : false;

type Test1 = IsCitrus; // true
type Test2 = IsCitrus; // false
type Test3 = IsCitrus; // false

在此示例中,IsCitrus 为 false,因为 Fruit 联合中并非所有水果都是 CitrusFruit。

最佳实践和技巧

  • 使用扩展来建立有意义的关系:仅当类型之间存在明确的“is-a”关系时才使用继承。
  • 优先选择组合而不是继承:在许多情况下,组合(使用接口和类型交集)比类继承更灵活。
  • 谨慎对待深层继承链:深层继承会使代码更难理解和维护。
  • 利用条件类型实现灵活的 API:使用带有扩展的条件类型来创建根据输入类型进行调整的 API。
  • 在泛型中使用扩展来创建可重用的、类型安全的函数:这允许您编写适用于各种类型的函数,同时仍然保持类型安全
版本声明 本文转载于:https://dev.to/hasanm95/mastering-typescript-understanding-the-power-of-extends-1n2o?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何解决 JLabel 拖放的鼠标事件冲突?
    如何解决 JLabel 拖放的鼠标事件冲突?
    用于拖放的 JLabel 鼠标事件:解决鼠标事件冲突为了在 JLabel 上启用拖放功能,鼠标事件必须被覆盖。然而,当尝试使用 mousePressed 事件实现拖放时,会出现一个常见问题,因为 mouseReleased 事件对该 JLabel 无效。提供的代码在 mousePressed 事件中...
    编程 发布于2024-11-06
  • MySQL 中的数据库分片:综合指南
    MySQL 中的数据库分片:综合指南
    随着数据库变得越来越大、越来越复杂,有效地控制性能和扩展就出现了。数据库分片是用于克服这些障碍的一种方法。称为“分片”的数据库分区将大型数据库划分为更小、更易于管理的段(称为“分片”)。通过将每个分片分布在多个服务器上(每个服务器保存总数据的一小部分),可以提高可扩展性和吞吐量。 在本文中,我们将探...
    编程 发布于2024-11-06
  • 如何将 Python 日期时间对象转换为秒?
    如何将 Python 日期时间对象转换为秒?
    在 Python 中将日期时间对象转换为秒在 Python 中使用日期时间对象时,通常需要将它们转换为秒以适应各种情况分析目的。但是,toordinal() 方法可能无法提供所需的输出,因为它仅区分具有不同日期的日期。要准确地将日期时间对象转换为秒,特别是对于 1970 年 1 月 1 日的特定日期...
    编程 发布于2024-11-06
  • 如何使用 Laravel Eloquent 的 firstOrNew() 方法有效优化 CRUD 操作?
    如何使用 Laravel Eloquent 的 firstOrNew() 方法有效优化 CRUD 操作?
    使用 Laravel Eloquent 优化 CRUD 操作在 Laravel 中使用数据库时,插入或更新记录是很常见的。为了实现这一点,开发人员经常求助于条件语句,在决定执行插入或更新之前检查记录是否存在。firstOrNew() 方法幸运的是, Eloquent 通过firstOrNew() 方...
    编程 发布于2024-11-06
  • 为什么在 PHP 中重写方法参数违反了严格的标准?
    为什么在 PHP 中重写方法参数违反了严格的标准?
    在 PHP 中重写方法参数:违反严格标准在面向对象编程中,里氏替换原则 (LSP) 规定:子类型的对象可以替换其父对象,而不改变程序的行为。然而,在 PHP 中,用不同的参数签名覆盖方法被认为是违反严格标准的。为什么这是违规?PHP 是弱类型语言,这意味着编译器无法在编译时确定变量的确切类型。这意味...
    编程 发布于2024-11-06
  • 哪个 PHP 库提供卓越的 SQL 注入防护:PDO 还是 mysql_real_escape_string?
    哪个 PHP 库提供卓越的 SQL 注入防护:PDO 还是 mysql_real_escape_string?
    PDO vs. mysql_real_escape_string:综合指南查询转义对于防止 SQL 注入至关重要。虽然 mysql_real_escape_string 提供了转义查询的基本方法,但 PDO 成为了一种具有众多优点的卓越解决方案。什么是 PDO?PHP 数据对象 (PDO) 是一个数...
    编程 发布于2024-11-06
  • React 入门:初学者的路线图
    React 入门:初学者的路线图
    大家好! ? 我刚刚开始学习 React.js 的旅程。这是一次令人兴奋(有时甚至具有挑战性!)的冒险,我想分享一下帮助我开始的步骤,以防您也开始研究 React。这是我的处理方法: 1.掌握 JavaScript 基础知识 在开始使用 React 之前,我确保温习一下我的 JavaScript 技...
    编程 发布于2024-11-06
  • 如何引用 JavaScript 对象中的内部值?
    如何引用 JavaScript 对象中的内部值?
    如何在 JavaScript 对象中引用内部值在 JavaScript 中,访问引用同一对象中其他值的对象中的值有时可能具有挑战性。考虑以下代码片段:var obj = { key1: "it ", key2: key1 " works!" }; ...
    编程 发布于2024-11-06
  • Python 列表方法快速指南及示例
    Python 列表方法快速指南及示例
    介绍 Python 列表用途广泛,并附带各种内置方法,有助于有效地操作和处理数据。下面是所有主要列表方法的快速参考以及简短的示例。 1. 追加(项目) 将项目添加到列表末尾。 lst = [1, 2, 3] lst.append(4) # [1, 2, 3, 4]...
    编程 发布于2024-11-06
  • C++ 中何时需要用户定义的复制构造函数?
    C++ 中何时需要用户定义的复制构造函数?
    何时需要用户定义的复制构造函数?复制构造函数是 C 面向对象编程的组成部分,提供了一种基于现有实例初始化对象的方法。虽然编译器通常会为类生成默认的复制构造函数,但在某些情况下需要进行自定义。需要用户定义复制构造函数的情况当默认复制构造函数不够时,程序员会选择用户定义的复制构造函数来实现自定义复制行为...
    编程 发布于2024-11-06
  • 尝试...捕获 V/s 安全分配 (?=):现代发展的福音还是诅咒?
    尝试...捕获 V/s 安全分配 (?=):现代发展的福音还是诅咒?
    最近,我发现了 JavaScript 中引入的新安全赋值运算符 (?.=),我对它的简单性着迷。 ? 安全赋值运算符 (SAO) 是传统 try...catch 块的简写替代方案。它允许您内联捕获错误,而无需为每个操作编写显式的错误处理代码。这是一个例子: const [error, respons...
    编程 发布于2024-11-06
  • 如何在Python中优化固定宽度文件解析?
    如何在Python中优化固定宽度文件解析?
    优化固定宽度文件解析为了有效地解析固定宽度文件,可以考虑利用Python的struct模块。此方法利用 C 来提高速度,如以下示例所示:import struct fieldwidths = (2, -10, 24) fmtstring = ' '.join('{}{}'.format(abs(fw...
    编程 发布于2024-11-06
  • 蝇量级
    蝇量级
    结构模式之一旨在通过与相似对象共享尽可能多的数据来减少内存使用。 在处理大量相似对象时特别有用,为每个对象创建一个新实例在内存消耗方面会非常昂贵。 关键概念: 内在状态:多个对象之间共享的状态独立于上下文,并且在不同对象之间保持相同。 外部状态:每个对象唯一的、从客户端传递的状态。此状态可能会有所不...
    编程 发布于2024-11-06
  • 解锁您的 MySQL 掌握:MySQL 实践实验室课程
    解锁您的 MySQL 掌握:MySQL 实践实验室课程
    通过全面的 MySQL 实践实验室课程提高您的 MySQL 技能并成为数据库专家。这种实践学习体验旨在指导您完成一系列实践练习,使您能够克服复杂的 SQL 挑战并优化数据库性能。 深入了解 MySQL 无论您是想要建立强大 MySQL 基础的初学者,还是想要提升专业知识的经验丰富的开...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3