TypeScript类型兼容:结构化类型

 🎬 岸边的风:个人主页

 🔥 个人专栏 :《 VUE 》 《 javaScript 》

⛺️ 生活的理想,就是为了理想的生活 !

在这里插入图片描述

目录

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

2. 鸭子类型的优点

2.1 代码的灵活性

2.2 代码的复用

2.3 与 JavaScript 的互操作性

3. 鸭子类型的局限性

3.1 类型安全

3.2 易读性和可维护性

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

4.1 清晰地定义接口

4.2 适度使用鸭子类型


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 对象,它有 walkquack 和 swim 这三个方法。尽管 myDuck 并没有显式地声明它实现了 Duck 接口,但是由于 myDuck 的结构满足了 Duck 接口的要求(即 myDuck 有 walk 和 quack 这两个方法),我们可以将 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 清晰地定义接口

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

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

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 的 "鸭子类型" 或 "结构化类型" 系统导致的。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Revit SDK 介绍:TypeRegeneration 修改类型,更新文档 ValidateParameters 参数合法性验证

前言 这篇文章介绍两个例子,逻辑比较简单: TypeRegeneration 修改类型,更新文档ValidateParameters 参数合法性验证 内容 TypeRegeneration FamilyType 不是继承自 Element 的,而是独立于 Element 体系之外,直接从…

RS485(一):电路与波形

一、RS485电路 ​RS485( Recommended Standard-485)是隶属于OSI模型-物理层的电气特性,规定为 2 线、半双工、平衡传输线的多点异步通信标准,通信采用差分信号传输。 典型485应用电路如下图所示: 其中 、# 分别控制接收和发送…

LeetCode 238. 除自身以外数组的乘积

题目链接 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题目解析 使用前缀和进行解决该题,只不过与之前前缀和不同的是这个题目计算前缀和的时候不需要计算当前元素,也就是当前位置前缀和的值其实是不包含当前元素的前缀和。…

C++内存管理

目录 一.new和delete 二.operator new与operator delete函数 三.new和delete的实现原理 四.在VS2022编译器下new和delete不匹配的问题 五.定位new 六.malloc/free和new/delete的区别 七.C内存分布 八.内存泄漏 C在内存管理上引入了两个操作符,分别是new,de…

Tailwind CSS 速成

Tailwind CSS 速成 完成了 responsive 和特效的学习后,现在折腾一下 tailwind CSS,这个 CSS 库本身就包含了很多的 utility class,之前跟着 yt 的视频写项目的时候,写了两个项目,好像不记得写过 CSS…… Redux Toolk…

SpringMVC的拦截器和JSR303的使用

目录 一、JSR303 二、拦截器(interceptor) 一、JSR303 1.1.什么是JSR303 JSR 303,它是Java EE(现在称为Jakarta EE)规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范,也称为Bean验证。 Bean验…

Golang goroutine 进程、线程、并发、并行

goroutine 看一个需求 需求:要求统计1-200000000000的数字中,哪些是素数? 分析思路: 1)传统的方法,就是使用一个循环,循环的判断各个数是不是素数(一个任务就分配给一个cpu去做,这样很不划算…

运动耳机哪个好、最好的运动牌子排名榜

很多朋友喜欢在运动的时候听音乐,为此,他们会为自己配备一款蓝牙耳机或是运动耳机,可以在运动的时候随身听,可是,一些人在挑选耳机的时候犯难了,市面上那么多运动耳机,运动耳机哪个好&#xff1…

关于rsync用不了之后

1.尝试找出rsync使用错误原因: 我遇见一个问题:rsync:read errors mapping:communication error on send (70),我查了一下这个问题很大可能是网络链接导致的,然后我用nslookup指令查看了/train2…

测试平台项目部署二(手动部署改成Dockerfile)

测试平台项目部署二(手动部署改成Dockerfile) 一、Dockerfile制作1、entrypoint.sh制作2、构建镜像3、启动容器二、遇到的问题1、pip install --no-cache-dir -r requirements.txt安装第三方库时,报Installing build dependencies: started2、安装第三方库文件比较慢,考虑更…

Vue3自定义指令

文章目录 Vue3自定义指令1. 自定义全局指令v-focus2. 自定义局部指令v-focus3. 指令定义的钩子函数3.1 概念3.2 钩子函数参数3.3 vnode & prevNode3.4 简写3.5 指令函数接受JavaScript表达式 Vue3自定义指令 1. 自定义全局指令v-focus 除了默认设置的核心指令( v-model 和…

zabbix -- 安装

Zabbix zabbix除了可以监控linux服务器之外,还可以监控路由器、交换机、容器等,全方位监控 Zabbix对服务器的监控是通过在服务器上部署“间谍”程序zabbix-agent获取数据,但对于路由器、交换机等机器的监控不能进行部署,这个时候…