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

掌握 TypeScript:了解扩展的力量

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

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]删除
最新教程 更多>
  • 如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    从python import codecs import codecs import codecs 导入 text = codecs.decode('这狗\ u0001f602'.encode('utf-8'),'utf-8') 印刷(文字)#带有...
    编程 发布于2025-06-08
  • Go语言垃圾回收如何处理切片内存?
    Go语言垃圾回收如何处理切片内存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片时,了解垃圾收集行为至关重要,以避免潜在的内存泄...
    编程 发布于2025-06-08
  • Python高效去除文本中HTML标签方法
    Python高效去除文本中HTML标签方法
    在Python中剥离HTML标签,以获取原始的文本表示Achieving Text-Only Extraction with Python's MLStripperTo streamline the stripping process, the Python standard librar...
    编程 发布于2025-06-08
  • Go web应用何时关闭数据库连接?
    Go web应用何时关闭数据库连接?
    在GO Web Applications中管理数据库连接很少,考虑以下简化的web应用程序代码:出现的问题:何时应在DB连接上调用Close()方法?,该特定方案将自动关闭程序时,该程序将在EXITS EXITS EXITS出现时自动关闭。但是,其他考虑因素可能保证手动处理。选项1:隐式关闭终止数...
    编程 发布于2025-06-08
  • MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    在两个条件下插入或更新或更新 solution:的答案在于mysql的插入中...在重复键更新语法上。如果不存在匹配行或更新现有行,则此功能强大的功能可以通过插入新行来进行有效的数据操作。如果违反了唯一的密钥约束。实现所需的行为,该表必须具有唯一的键定义(在这种情况下为'名称'...
    编程 发布于2025-06-08
  • 查找当前执行JavaScript的脚本元素方法
    查找当前执行JavaScript的脚本元素方法
    如何引用当前执行脚本的脚本元素在某些方案中理解问题在某些方案中,开发人员可能需要将其他脚本动态加载其他脚本。但是,如果Head Element尚未完全渲染,则使用document.getElementsbytagname('head')[0] .appendChild(v)的常规方...
    编程 发布于2025-06-08
  • 为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    在CSS 问题:不正确的代码: 全球范围将所有余量重置为零,如提供的代码所建议的,可能会导致意外的副作用。解决特定的保证金问题是更建议的。 例如,在提供的示例中,将以下代码添加到CSS中,将解决余量问题: body H1 { 保证金顶:-40px; } 此方法更精确,避免了由全局保证金重置引...
    编程 发布于2025-06-08
  • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用时间数据时...
    编程 发布于2025-06-08
  • 将图片浮动到底部右侧并环绕文字的技巧
    将图片浮动到底部右侧并环绕文字的技巧
    在Web设计中围绕在Web设计中,有时可以将图像浮动到页面右下角,从而使文本围绕它缠绕。这可以在有效地展示图像的同时创建一个吸引人的视觉效果。 css位置在右下角,使用css float and clear properties: img { 浮点:对; ...
    编程 发布于2025-06-08
  • 用户本地时间格式及时区偏移显示指南
    用户本地时间格式及时区偏移显示指南
    在用户的语言环境格式中显示日期/时间,并使用时间偏移在向最终用户展示日期和时间时,以其localzone and格式显示它们至关重要。这确保了不同地理位置的清晰度和无缝用户体验。以下是使用JavaScript实现此目的的方法。方法:推荐方法是处理客户端的Javascript中的日期/时间格式化和时...
    编程 发布于2025-06-08
  • Python中何时用"try"而非"if"检测变量值?
    Python中何时用"try"而非"if"检测变量值?
    使用“ try“ vs.” if”来测试python 在python中的变量值,在某些情况下,您可能需要在处理之前检查变量是否具有值。在使用“如果”或“ try”构建体之间决定。“ if” constructs result = function() 如果结果: 对于结果: ...
    编程 发布于2025-06-08
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-06-08
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在银光应用程序中,尝试使用LINQ建立错误的数据库连接的尝试,无法找到以查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中,tblpersoon可能需...
    编程 发布于2025-06-08
  • PHP与C++函数重载处理的区别
    PHP与C++函数重载处理的区别
    作为经验丰富的C开发人员脱离谜题,您可能会遇到功能超载的概念。这个概念虽然在C中普遍,但在PHP中构成了独特的挑战。让我们深入研究PHP功能过载的复杂性,并探索其提供的可能性。在PHP中理解php的方法在PHP中,函数超载的概念(如C等语言)不存在。函数签名仅由其名称定义,而与他们的参数列表无关。...
    编程 发布于2025-06-08

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

Copyright© 2022 湘ICP备2022001581号-3