TypeScript 结构类型的应用

TypeScript 结构类型的应用

TypeScript是JavaScript的超集,为JavaScript引入了静态类型,允许开发者捕捉潜在错误并增强代码质量。 TypeScript与其他静态类型语言的一个关键特征是它对结构类型的支持。虽然其他静态类型语言通常依靠名义类型(nominal typing),但TypeScript则采用了结构类型的概念,这提供了一种更灵活和直观的类型检查方式。

在本教程中,我们将探讨TypeScript中结构类型的概念及其优势,并提供相关示例来说明其用法。

理解结构类型

结构类型是一种类型系统,关注的是对象的形状或结构,而不是其具体的名称或类。换句话说,如果两个对象具有相同的属性和方法集合,无论其显式声明或继承关系如何,它们都被认为是兼容的。这种方法促进了代码的可重用性,鼓励使用duck类型(duck typing),其中一个对象的适用性是通过其行为而不是其类或类型来确定的。

什么是duck类型

duck类型是编程中的一个概念,侧重于对象的行为而不是其具体类型或类。该术语”duck类型”起源于短语”If it looks like a duck and quacks like a duck, then it’s probably a duck.”(如果它看起来像duck,嘎嘎叫起来像duck,那么它可能就是一只duck)。换句话说,duck类型是基于对象是否支持所需的方法和属性来确定对象的适用性,而不是依赖于显式的类型声明。

结构类型的优势

示例1

duck类型和多态性 − 结构类型使得duck类型成为可能,允许开发者编写更加灵活和可重用的代码。多态性是面向对象编程的一个基本原则,结构相匹配的对象可以互换使用。

在下面的示例中,printItem函数接受一个Printable类型的对象。任何具有print方法的对象都可以传递给该函数,无论其显式声明或类如何。

interface Printable {
   print(): void;
}

class Doc implements Printable {
   print() {
      console.log("Printing document...");
   }
}

class DocExtended implements Printable {
   print(): void {
      console.log("Printing from extended documented...");
   }
   wow(): void {
      console.log("This works!!");
   }
}

function printItem(item: Printable) {
   item.print();
}

const doc = new Doc();
printItem(doc);
const docExtended = new DocExtended();
printItem(docExtended);

在编译时,它将生成以下的JavaScript代码−

var Doc = /** @class */ (function () {
   function Doc() {
   }
   Doc.prototype.print = function () {
      console.log("Printing document...");
   };
   return Doc;
}());
var DocExtended = /** @class */ (function () {
   function DocExtended() {
   }
   DocExtended.prototype.print = function () {
      console.log("Printing from extended documented...");
   };
   DocExtended.prototype.wow = function () {
      console.log("This works!!");
   };
   return DocExtended;
}());
function printItem(item) {
   item.print();
}
var doc = new Doc();
printItem(doc);
var docExtended = new DocExtended();
printItem(docExtended);

输出

上述代码将产生以下输出 −

Printing document...
Printing from extended documented...

示例2

结构子类型 - 结构类型支持隐式接口,也被称为结构子类型。 TypeScript允许对象在没有显式定义接口的情况下隐式符合预期的结构,从而简化了代码维护,减少了显式接口声明的需求。

在下面的示例中,logMessage函数期望一个具有类型为字符串的text属性的对象。 TypeScript根据对象的结构推断出类型,使我们能够直接传递一个对象字面量而无需定义其类型。

function logMessage(message: { text: string }) {
   console.log(message.text);
}

const message = { text: "Hello, world!" };
logMessage(message);

在编译时,它将生成以下的JavaScript代码−

function logMessage(message) {
   console.log(message.text);
}
var message = { text: "Hello, world!" };
logMessage(message);

输出

上述代码将产生以下输出 –

Hello, world!

示例3

灵活的类型兼容性 - 结构化类型在类型兼容性方面提供了更大的灵活性。即使显式类型不同,具有相同结构的两个对象也可以互相赋值。这有助于在应用程序的不同部分之间实现互操作性和代码重用。

在上面的示例中,将一个Circle对象分配给Shape类型的变量,因为Circle的结构与Shape接口中定义的属性匹配。一旦将其定义为Shape类型,就无法访问在Circle类中定义的sayHello方法或属性。因此,无法在shape对象上访问sayHello函数。

interface Shape {
   color: string;
   display: () =&g void;
}

class Circle {
   color: string;
   radius: number;

   constructor(color: string, radius: number) {
      this.color = color;
      this.radius = radius;
   }

   display(): void {
      console.log(`The value of color is: {this.color}`);
      console.log(`The value of radius is:{this.radius}`);
   }
   sayHello() {
      console.log(
         "Hey there! I am a circle but still compatible with Shape interface..."
      );
   }
}
const shape: Shape = new Circle("red", 5);
shape.display();

在编译时,它将生成以下的JavaScript代码−

var Circle = /** @class */ (function () {
   function Circle(color, radius) {
      this.color = color;
      this.radius = radius;
   }
   Circle.prototype.display = function () {
      console.log("The value of color is: ".concat(this.color));
      console.log("The value of radius is: ".concat(this.radius));
   };
   Circle.prototype.sayHello = function () {
      console.log("Hey there! I am a circle but still compatible with Shape interface...");
   };
   return Circle;
}());
var shape = new Circle("red", 5);
shape.display();

输出

上述代码将产生以下输出 –

The value of color is: red
The value of radius is: 5

示例4

开放可扩展系统 − 结构化类型允许创建开放和可扩展的系统,可以无缝地添加和集成新类型。由于兼容性基于对象的结构,向现有对象添加新属性或方法不会破坏与代码库其他部分的兼容性。这使得可以更容易地演化和扩展代码,而不会在整个系统中引起级联更改。

在此示例中,即使圆对象具有额外的半径属性,只要它具有颜色属性,它仍然与Shape接口兼容。

interface Shape {
   color: string;
}

function printShapeColor(shape: Shape) {
   console.log(shape.color);
}
const circle = { color: "blue", radius: 5 };
printShapeColor(circle); // Prints "blue"

编译后,会生成以下的JavaScript代码:

function printShapeColor(shape) {
   console.log(shape.color);
}
var circle = { color: "blue", radius: 5 };
printShapeColor(circle); // Prints "blue"

输出

上述代码将产生以下输出−

blue

示例5

隐式转换和互操作性 − 结构类型使得具有兼容结构的类型之间能够进行隐式转换。这使得更容易使用库或来自外部源代码的代码,这些代码可能没有明确匹配预期类型。TypeScript可以自动推断出结构的兼容性并执行必要的转换,而不需要明确的类型注释或转换。

在此示例中,customer对象具有额外的address属性,但是TypeScript仍然可以推断它与Person接口的兼容性,使得它可以在不出错的情况下传递给greet函数。

interface Person {
   name: string;
   age: number;
}

function greet(person: Person) {
   console.log(`Hello, {person.name}! You are{person.age} years old.`);
}
const customer = { name: "Alice", age: 30, address: "123 Street" };
greet(customer); // Prints "Hello, Alice! You are 30 years old."

编译后,它会生成以下JavaScript代码:

function greet(person) {
   console.log("Hello, ".concat(person.name, "! You are ").concat(person.age, " years old."));
}
var customer = { name: "Alice", age: 30, address: "123 Street" };
greet(customer); // Prints "Hello, Alice! You are 30 years old."

输出

上面的代码将产生以下输出−

Hello, Alice! You are 30 years old.

示例 6

与现有 JavaScript 代码轻松集成 - TypeScript 的结构类型使得与现有的 JavaScript 代码库轻松集成成为可能。由于 JavaScript 是动态类型的,并且经常依赖于duck类型,结构类型与 JavaScript 的运行时行为相吻合。开发人员可以逐步将 TypeScript 引入到他们的 JavaScript 项目中,而不需要立即为所有对象定义明确的接口。

在这个 JavaScript 代码片段中,没有明确的类型或接口定义。TypeScript 可以推断出 greeting 对象的结构,并且在与 printMessage 函数交互时确保类型安全。

// JavaScript code
function printMessage(message) {
   console.log(message);
}

const greeting = { text: "Hello, world!" };
printMessage(greeting); // Prints "{ text: "Hello, world!" }"

在编译时,它将生成以下JavaScript代码 –

// JavaScript code
function printMessage(message) {
   console.log(message);
}
var greeting = { text: "Hello, world!" };
printMessage(greeting); // Prints "{ text: "Hello, world!" }"

输出

上面的代码将产生以下输出-

{ text: 'Hello, world!' }

结论

TypeScript中的结构类型是一个强大的功能,它促进了灵活性、代码重用和互操作性。通过关注对象的结构而不是其显式类型,TypeScript使开发人员能够编写更具表现力和适应性的代码。利用duck类型,实现多态性和享受灵活的类型兼容性的能力,在代码可维护性和可扩展性方面提供了显著的优势。在继续学习TypeScript的过程中,接受结构类型可以帮助您构建强大且灵活的应用程序。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程

TypeScript 精选笔记