《现代Typescript高级教程》结构化类型

简介: 类型兼容:结构化类型TypeScript 是一种基于 JavaScript 的静态类型语言,它为 JavaScript 添加了类型系统,并提供了强大的类型检查和自动补全功能。TypeScript 的类型系统有一个非常重要的特性,那就是 "鸭子类型"(Duck Typing)或 "结构化类型"(Structural Typing)(文章会以"鸭子类型"(Duck Typing)作为简称)。这种特性有时会让人感到惊讶,但它是 TypeScript 增强 JavaScript 开发体验的重要方式之一。

类型兼容:结构化类型

TypeScript 是一种基于 JavaScript 的静态类型语言,它为 JavaScript 添加了类型系统,并提供了强大的类型检查和自动补全功能。TypeScript 的类型系统有一个非常重要的特性,那就是 "鸭子类型"(Duck Typing)或 "结构化类型"(Structural Typing)(文章会以"鸭子类型"(Duck Typing)作为简称)。这种特性有时会让人感到惊讶,但它是 TypeScript 增强 JavaScript 开发体验的重要方式之一。

鸭子类型的概念来自一个古老的英语成语:“如果它走起路来像一只鸭子,叫起来也像一只鸭子,那么它就是一只鸭子。”在 TypeScript(或更一般地说,静态类型语言)的上下文中,鸭子类型意味着一个对象的类型不是由它继承或实现的具体类别决定的,而是由它具有的结构决定的。

本文将全面深入地探讨 TypeScript 中的鸭子类型,以及如何在实际的开发中应用和利用鸭子类型。

1. 鸭子类型:定义和示例

鸭子类型的概念来自一个古老的英语成语:“如果它走起路来像一只鸭子,叫起来也像一只鸭子,那么它就是一只鸭子。”在 TypeScript(或更一般地说,静态类型语言)的上下文中,鸭子类型意味着一个对象的类型不是由它继承或实现的具体类别决定的,而是由它具有的结构决定的。

这是一个简单的鸭子类型示例:

interface Duck {
    walk: () => void;
    quack: () => void;
}
function doDuckThings(duck: Duck) {
    duck.walk();
    duck.quack();
}
const myDuck = {
    walk: () => console.log('Walking like a duck'),
    quack: () => console.log('Quacking like a duck'),
    swim: () => console.log('Swimming like a duck')
};
doDuckThings(myDuck); // OK

在这个例子中,我们定义了一个 Duck 接口和一个 doDuckThings 函数,这个函数需要一个 Duck 类型的参数。然后我们创建了一个 myDuck 对象,它有 walkquackswim 这三个方法。尽管 myDuck 并没有显式地声明它实现了 Duck 接口,但是由于 myDuck 的结构满足了 Duck 接口的要求(即 myDuckwalkquack 这两个方法),我们可以将 myDuck 作为参数传递给 doDuckThings 函数。

这就是鸭子类型的基本概念:只要一个对象的结构满足了接口的要求,我们就可以把这个对象看作是这个接口的实例,而不管这个对象的实际类型是什么。

2. 鸭子类型的优点

鸭子类型有许多优点,特别是在编写更灵活和更通用的代码方面。

2.1 代码的灵活性

鸭子类型增加了代码的灵活性。我们可以创建和使用满足特定接口的任何对象,而不必担心它们的具体类型。这使得我们可以更容易地编写通用的代码,因为我们的代码只依赖于对象的结构,而不是对象的具体类型。

2.2 代码的复用

鸭子类型有助于代码的复用。由于我们的函数和方法只依赖于对象的结构,我们可以在不同的上下文中重用这些函数和方法,只要传入的对象满足所需的结构。

例如,我们可以写一个函数,它接受一个具有 toString 方法的任何对象,然后返回这个对象的字符串表示。由于几乎所有的 JavaScript 对象都有 toString 方法,我们可以在许多不同的上下文中重用这个函数。

function toString(obj: { toString: () => string }) {
    return obj.toString();
}
console.log(toString(123)); // "123"
console.log(toString([1, 2, 3])); // "1,2,3"
console.log(toString({ a: 1, b: 2 })); // "[object Object]"

2.3 与 JavaScript 的互操作性

鸭子类型提高了 TypeScript 与 JavaScript 的互操作性。由于 JavaScript 是一种动态类型语言,我们经常需要处理的对象可能没有明确的类型。鸭子类型使我们能够在 TypeScript 中安全地处理这些对象,只要它们的结构满足我们的需求。

例如,我们可能从一个 JavaScript 库获取一个对象,这个对象有一个 forEach 方法。我们不关心这个对象的具体类型,我们只关心它是否有 forEach 方法。使用鸭子类型,我们可以定义一个接口来描述这个对象的结构,然后在 TypeScript 中安全地使用这个对象。

interface Iterable {
    forEach: (callback: (item: any) => void) => void;
}
function processItems(iterable: Iterable) {
    iterable.forEach(item => console.log(item));
}
const jsArray = [1, 2, 3]; // From a JavaScript library
processItems(jsArray); // OK

3. 鸭子类型的局限性

尽管鸭子类型有许多优点,但它也有一些局限性。

3.1 类型安全

鸭子类型可能会降低代码的类型安全性。因为 TypeScript 的类型检查器只检查对象是否满足接口的结构,而不检查对象是否真的是接口所期望的类型。如果一个对象恰好有与接口相同的属性和方法,但实际上它并不是接口所期望的类型,TypeScript 的类型检查器可能无法发现这个错误。

例如,我们可能有一个 Dog 类型和一个 Cat 类型,它们都有一个 bark 方法。我们可能会错误地将一个 `Cat

对象传递给一个期望Dog` 对象的函数,而 TypeScript 的类型检查器无法发现这个错误

interface Dog {
    bark: () => void;
}
function letDogBark(dog: Dog) {
    dog.bark();
}
const cat = {
    bark: () => console.log('Meow...'),  // Cats don't bark!
    purr: () => console.log('Purr...')
};
letDogBark(cat); // No error, but it's wrong!

在这种情况下,我们需要更仔细地设计我们的类型和接口,以避免混淆。

3.2 易读性和可维护性

鸭子类型可能会降低代码的易读性和可维护性。因为我们的代码只依赖于对象的结构,而不是对象的具体类型,这可能会使代码更难理解和维护。

为了提高易读性和可维护性,我们需要清晰地记录我们的接口和函数期望的对象结构。TypeScript 的类型注解和接口提供了一种强大的工具来实现这一点。

4. 使用鸭子类型的最佳实践

在使用鸭子类型时,有一些最佳实践可以帮助我们避免上述问题,并充分利用鸭子类型的优点。

4.1 清晰地定义接口

我们应该清晰地定义我们的接口,以描述我们的函数和方法期望的对象结构。这有助于提高代码的易读性和可维护性。

例如,如果我们有一个函数,它期望一个具有 nameage 属性的对象,我们应该定义一个接口来描述这个结构。

interface Person {
    name: string;
    age: number;
}
function greet(person: Person) {
    console.log(`Hello, my name is ${person.name} and I'm ${person.age} years old.`);
}

4.2 适度使用鸭子类型

我们应该适度地使用鸭子类型。虽然鸭子类型有许多优点,但如果过度使用,可能会导致类型安全性的问题,以及易读性和可维护性的降低。我们应该在类型安全性、易读性、可维护性和灵活性之间找到一个平衡。

在某些情况下,我们可能更希望使用类和继承,而不是鸭子类型。例如,如果我们有一组紧密相关的类型,它们有共享的行为和状态,使用类和继承可能更合适。

interface Named {
    name: string;
}
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
}
let p: Named;
// OK, because of structural typing
p = new Person('mike');

在这个例子中,尽管 Person 类并没有显式地实现 Named 接口,但是因为 Person 类有一个 name 属性,所以我们可以把 Person 的实例赋值给 Named 类型的变量。这是由于 TypeScript 的 "鸭子类型" 或 "结构化类型" 系统导致的。

目录
相关文章
|
9天前
|
JavaScript
typeScript进阶(9)_type类型别名
本文介绍了TypeScript中类型别名的概念和用法。类型别名使用`type`关键字定义,可以为现有类型起一个新的名字,使代码更加清晰易懂。文章通过具体示例展示了如何定义类型别名以及如何在函数中使用类型别名。
25 1
typeScript进阶(9)_type类型别名
|
9天前
|
JavaScript
typeScript基础(2)_any任意值类型和类型推论
本文介绍了TypeScript中的`any`任意值类型,它可以赋值为其他任何类型。同时,文章还解释了TypeScript中的类型推论机制,即在没有明确指定类型时,TypeScript如何根据变量的初始赋值来推断其类型。如果变量初始化时未指定类型,将被推断为`any`类型,从而允许赋予任何类型的值。
25 4
|
9天前
|
JavaScript
typeScript基础(5)_对象的类型-interfaces接口
本文介绍了TypeScript中接口(interfaces)的基本概念和用法,包括如何定义接口、接口的简单使用、自定义属性、以及如何使用`readonly`关键字定义只读属性。接口在TypeScript中是定义对象形状的重要方式,可以规定对象的必有属性、可选属性、自定义属性和只读属性。
23 1
|
9天前
|
存储 JavaScript
typeScript进阶(11)_元组类型
本文介绍了TypeScript中的元组(Tuple)类型,它是一种特殊的数组类型,可以存储不同类型的元素。文章通过示例展示了如何声明元组类型以及如何给元组赋值。元组类型在定义时需要指定数组中每一项的类型,且在赋值时必须满足这些类型约束。此外,还探讨了如何给元组类型添加额外的元素,这些元素必须符合元组类型中定义的类型联合。
20 0
|
9天前
|
JavaScript
typeScript进阶(10)_字符串字面量类型
本文介绍了TypeScript中的字符串字面量类型,这种类型用来限制变量只能是某些特定的字符串字面量。通过使用`type`关键字声明,可以确保变量的值限定在预定义的字符串字面量集合中。文章通过示例代码展示了如何声明和使用字符串字面量类型,并说明了它在函数默认参数中的应用。
21 0
|
9天前
|
JavaScript 前端开发
typeScript基础(8)_ts类型断言
本文介绍了TypeScript中的类型断言,它用于在编译时告诉TypeScript某个对象具有特定的类型,即使它看起来不具备。类型断言可以用来访问一个类型上存在而另一个类型上不存在的属性或方法。需要注意的是,类型断言并不会在运行时改变JavaScript的行为,因此如果断言不当,运行时仍然可能出错。文章还提醒避免将类型断言为`any`类型或进行多重断言。
12 1
|
9天前
|
JavaScript
typeScript基础(6)_数组类型
本文介绍了TypeScript中数组的类型表示方法,包括直接使用类型加`[]`定义数组类型,以及使用数组泛型`Array<类型>`定义数组。同时,还展示了如何定义包含多种数据类型的数组。
20 1
|
6天前
|
JavaScript 前端开发 编译器
TypeScript,从0到入门带你进入类型的世界
该文章提供了TypeScript的入门指南,从安装配置到基础语法,再到高级特性如泛型、接口等的使用,帮助初学者快速掌握TypeScript的基本用法。
|
9天前
|
JavaScript
typeScript基础(7)_函数的类型
本文介绍了TypeScript中函数的类型,包括函数声明与函数表达式的类型注解,如何定义函数的参数类型、返回类型,以及可选参数和参数默认值。还探讨了函数的剩余参数、如何使用接口定义函数的形状,以及函数重载的概念和实践。
10 0
|
2月前
|
JavaScript 前端开发 编译器
Angular 与 TypeScript 强强联手太厉害啦!强类型编程带来巨大开发优势,快来一探究竟!
【8月更文挑战第31天】作为一名前端开发者,我致力于探索各种技术框架以提升开发效率与代码质量。近期深入研究了Angular与TypeScript的结合,体验到强类型编程带来的显著优势。Angular是一款强大的前端框架,而TypeScript则是由微软开发的一种强类型语言,为JavaScript增添了静态类型检查等功能。
24 0
下一篇
无影云桌面