TypeScript类型兼容:协变和逆变

news2024/11/29 9:42:42

 🎬 岸边的风:个人主页

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

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

在这里插入图片描述

目录

引言

协变(Covariance)

协变:类型的向下兼容性

逆变(Contravariance)

逆变:类型的向上兼容性

协变与逆变的平衡


 

引言

在类型系统中,协变和逆变是对类型比较(类型兼容)一种形式化描述。在一些类型系统中,例如 Java,这些概念是显式嵌入到语言中的,例如使用extends关键字表示协变,使用super关键字表示逆变。在其他一些类型系统中,例如 TypeScript,协变和逆变的规则是隐式嵌入的,通过类型兼容性检查来实现。

协变和逆变的存在使得类型系统具有更大的灵活性。例如,如果你有一个Animal类型的数组,并且你有一个Dog类型的对象(假设DogAnimal的子类型),那么你应该能够将Dog对象添加到Animal数组中。这就是协变。反过来,如果你有一个处理Animal类型对象的函数,并且你有一个Dog类型的对象,你应该可以使用这个函数来处理Dog对象。这就是逆变。

协变和逆变还可以帮助我们创建更通用的代码。例如,如果你有一个可以处理任何Animal的函数,那么这个函数应该能够处理任何Animal的子类型。这意味着,你可以编写一段只依赖于Animal类型的代码,然后使用这段代码处理任何Animal的子类型。

协变(Covariance)

协变描述的是如果存在类型A和B,并且A是B的子类型,那么我们就可以说由A组成的复合类型(例如Array<A>或者(a: A) => void)也是由B组成的相应复合类型(例如Array<B>或者(b: B) => void)的子类型。

让我们通过一个例子来理解协变。假设我们有两个类型AnimalDog,其中DogAnimal的子类型。

type Animal = { name: string };
type Dog = Animal & { breed: string };

let dogs: Dog[] = [{ name: "Fido", breed: "Poodle" }];
let animals: Animal[] = dogs;  // OK because Dog extends Animal, Dog[] is a subtype of Animal[]

这里我们可以将类型为Dog[]dogs赋值给类型为Animal[]animals,因为Dog[]Animal[]的子类型,所以数组是协变的。

协变:类型的向下兼容性

协变是类型系统中的一个基本概念,它描述的是类型的“向下兼容性”。如果一个类型A可以被看作是另一个类型B的子类型(即A可以被安全地用在期望B的任何地方),那么我们就说A到B是协变的。这是类型系统中最常见和直观的一种关系,例如在面向对象编程中的继承就是协变的一种表现。

在TypeScript中,所有的类型都是自身的子类型(即每个类型到自身是协变的),并且nullundefined类型是所有类型的子类型。除此之外,接口和类也可以通过继承来形成协变关系。

class Animal {
  name: string;
}

class Dog extends Animal {
  breed: string;
}

let myDog: Dog = new Dog();
let myAnimal: Animal = myDog;  // OK,因为Dog是Animal的子类型

这个例子中,我们可以将一个Dog对象赋值给一个Animal类型的变量,因为DogAnimal是协变的。

在TypeScript中,泛型类型也是协变的。例如,如果类型A是类型B的子类型,那么Array<A>就是Array<B>的子类型。

let dogs: Array<Dog> = [new Dog()];
let animals: Array<Animal> = dogs;  // OK,因为Array<Dog>是Array<Animal>的子类型

逆变(Contravariance)

逆变是协变的反面。如果存在类型A和B,并且A是B的子类型,那么我们就可以说由B组成的某些复合类型是由A组成的相应复合类型的子类型。

这在函数参数中最常见。让我们来看一个例子:

type Animal = { name: string };
type Dog = Animal & { breed: string };

let dogHandler = (dog: Dog) => { console.log(dog.breed); }
let animalHandler: (animal: Animal) => void = dogHandler;  // Error! 

在这个例子中,我们不能将类型为(dog: Dog) => voiddogHandler赋值给类型为(animal: Animal) => voidanimalHandler。因为如果我们传递一个Animal(并非所有的Animal都是Dog)给animalHandler,那么在执行dogHandler函数的时候,就可能会引用不存在的breed属性。因此,函数的参数类型是逆变的。

逆变:类型的向上兼容性

逆变描述的是类型的“向上兼容性”。如果一个类型A可以被看作是另一个类型B的超类型(即B可以被安全地用在期望A的任何地方),那么我们就说A到B是逆变的。在函数参数类型的兼容性检查中,TypeScript使用了逆变。

type Handler = (arg: Dog) => void;

let animalHandler: Handler = (animal: Animal) => { /* ... */ };
let dogHandler: Handler = (dog: Dog) => { /* ... */ };  // OK,因为Animal是Dog的超类型

这个例子中,我们可以将一个处理`

Dog的函数赋值给一个处理Animal的函数类型的变量,因为AnimalDog的超类型,所以(dog: Dog) => void类型是(animal: Animal) => void`类型的子类型。

这看起来可能有些反直觉,但实际上是为了保证类型安全。因为在执行dogHandler函数时,我们可以安全地传入一个Animal对象,而不需要担心它可能不是Dog类型。

协变与逆变的平衡

协变和逆变在大多数情况下都可以提供合适的类型检查,但是它们并非完美无缺。在实际应用中,我们必须关注可能的边界情况,以避免运行时错误。在某些情况下,我们甚至需要主动破坏类型的协变或逆变,以获得更强的类型安全。例如,如果我们需要向一个Dog[]数组中添加Animal对象,我们可能需要将这个数组的类型声明为Animal[],以防止添加不兼容的类型。

总的来说,协变和逆变是理解和应用TypeScript类型系统的重要工具,但我们必须在灵活性和类型安全之间找到合适的平衡。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

deepspeed训练报错torch.distributed.elastic.multiprocessing.errors.ChildFailedError

测试场景&#xff1a;使用deepspeed框架训练gpt模型 问题&#xff1a; 报错torch.distributed.elastic.multiprocessing.errors.ChildFailedError 具体见截图&#xff1a;

随手笔记(四十五)——idea git冲突

图片为引用&#xff0c;在一次导入项目至gitee的过程中&#xff0c;不知道为什么报了403&#xff0c;很奇怪的一个错误&#xff0c;网上很多的答案大概分成两种。 第一种是最多的&#xff0c;直接找到windows凭据删掉 很抱歉的告诉各位&#xff0c;你们很多人到这里就已经解…

线性代数的本质(四)——行列式

文章目录 行列式二阶行列式 n n n 阶行列式行列式的性质克拉默法则行列式的几何理解 行列式 二阶行列式 行列式引自对线性方程组的求解。考虑两个方程的二元线性方程组 { a 11 x 1 a 12 x 2 b 1 a 21 x 1 a 22 x 2 b 2 \begin{cases} a_{11}x_1a_{12}x_2b_1 \\ a_{21}x_…

Vue3:proxy数据取值proxy[Target]取值

vue3底层是使用proxy进行代理的&#xff0c;而proxy中[[Target]]才是想要的值。 获取target值的方式一&#xff1a; <script setup>//先引入toRawimport { toRaw } from vue;//再使用console.log(toRaw(数据名))</script> 获取target值的方式二&#xff1a; <…

阿里云服务器配置怎么选择?几核几G?带宽系统盘怎么选?

阿里云服务器配置选择_CPU内存/带宽/存储配置_小白指南&#xff0c;阿里云服务器配置选择方法包括云服务器类型、CPU内存、操作系统、公网带宽、系统盘存储、网络带宽选择、安全配置、监控等&#xff0c;阿小云分享阿里云服务器配置选择方法&#xff0c;选择适合自己的云服务器…

NetSuite知识会汇编-管理员篇顾问篇2023

本月初&#xff0c;开学之际&#xff0c;我们发布了《NetSuite知识会汇编-用户篇 2023》&#xff0c;这次发布《NetSuite知识会汇编-管理员篇&顾问篇2023》。本篇挑选了近两年NetSuite知识会中的一些文章&#xff0c;涉及开发、权限、系统管理等较深的内容&#xff0c;共19…

嵌入式Linux驱动开发(I2C专题)(二)

I2C系统的重要结构体 参考资料&#xff1a; Linux驱动程序: drivers/i2c/i2c-dev.cI2CTools: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/ 1. I2C硬件框架 2. I2C传输协议 3. Linux软件框架 4. 重要结构体 使用一句话概括I2C传输&#xff1a;APP通过I2…

linux rz乱码文件删除

通过rz上传文件的时候经常会遇到 文件乱码问题&#xff0c;删又删不掉。 使用rz -be的方法上传 遇到乱码文件操作步骤 1. ls -i # 列出文件的编号 2. find . -inum 29229139 -delete # 根据编号删除文件 find 需要扫描的路径 -inum 文件编号 -delete

VRTK4⭐四.和 UI 元素交互

文章目录 &#x1f7e5; 安装Tilia Unity.UI&#x1f7e7; 配置射线与UI交互器1️⃣ 配置直线射线2️⃣ 配置UI交互器 &#x1f7e8; 配置UI1️⃣ 更新EventSystem2️⃣ 进行Canvas设置 我们要实现的功能: 右手触摸到圆盘:显示直线射线 右手圆盘键按下:与选中UI交互 &#x1f7…

JWT安全

文章目录 理论知识cookie(放在浏览器)session(放在 服务器)tokenjwt&#xff08;json web token&#xff09;headerpayloadSignatureJWT通信流程 JWT与Token 区别相同点区别 WebGoat靶场--JWT tokens环境启动第四关第五关第七关 属于越权漏洞 理论知识 cookie(放在浏览器) ​…

静电消除器在工业设备中的用途

静电消除有几种&#xff0c;其中包括离子风枪、离子风嘴、离子风棒、离子风枪、离子风蛇等。今天我们就来聊一下离子风蛇。 离子风蛇是一种坐立式静电消除器&#xff0c;可消除难以接近或接触物体的静电或灰尘&#xff0c;可将蛇管随意变形使风咀指向目标方位&#xff0c;无需…

AIGC:【LLM(八)】——Baichuan2技术报告

文章目录 摘要1. 引言2. 预训练2.1 预训练数据&#xff08;Pre-training Data&#xff09;2.2 架构&#xff08;Architecture&#xff09;2.3 令牌化器&#xff08;Tokenizer&#xff09;2.3.1 Positional Embeddings 2.4 激活和规范化&#xff08;Activations and Normalizati…

vue2-x6-dag自定义vue组件节点

效果如图 官方案例 人工智能建模 DAG 图 vue2中自定义节点 代码 1.dag.json [{"id": "1","shape": "dag-node","x": 290,"y": 110,"data": {"label": "读数据","status&q…

2023CVPR:图像恢复的又一力作

今天要分享的论文是2023CVPR《Efficient and Explicit Modelling of Image Hierarchies for Image Restoration》&#xff0c;图像恢复领域的又一力作&#xff0c;提供了新的发现&#xff0c;给出了新的解决方案 代码 https://github.com/ofsoundof/GRL-Image-Restoration 问…

C语言指针快速入门

指针的基本介绍 简单的说指针用于表示地址&#xff0c;存放的是一个地址 获取指针的地址 //指针的入门 #include <stdio.h>int main() {int num 1;//num的地址是多少//说明1&#xff1a;如果要输出一个变量的地址使用的格式是%p//说明2&#xff1a;&num 表示取出n…

百度SEO优化攻略(提高网站排名的必修课)

百度SEO优化策略介绍&#xff1a; 在百度搜索引擎中&#xff0c;网站的排名越靠前&#xff0c;就越能吸引更多的流量和用户。要想让网站排名更高&#xff0c;就必须进行SEO优化。百度SEO优化是一个长期的过程&#xff0c;需要不断调整和优化。下面介绍5个优化方法&#xff0c;…

C++ - 二叉树OJ题

二叉树的两种层序遍历 在写之前&#xff0c;我们先来看两种二叉树的层序遍历&#xff1a; 1.给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 输入&#xff1a;root [3,9,20,null,null…

靠差异化上了短剧“牌桌”后,百度准备怎么做生态?

从最初的野蛮生长到如今的百花齐放&#xff0c;短剧市场已然进入了质量与创意的竞争。 据《中国网络视听发展研究报告》数据显示&#xff0c;行业内重点网络微短剧上线数量从2021年的58部&#xff0c;飙升到2022年的172部。相比起前几年处于风口时的爆发式增长&#xff0c;“分…

广读论文核心思路汇总笔记 (一些有意思的论文and论文在研究的一些有意思的问题or场景应用)

思路可借鉴和学习 On the Generalization of Multi-modal Contrastive Learning CoRR, ICML&#xff08;2023&#xff09; 摘要&#xff1a;多模态对比学习&#xff08;MMCL&#xff09;最近引起了广泛关注&#xff0c;因为它在视觉任务上的表现优于其他方法&#xff0c;这些方…

ASP.NET dotnet 3.5 实验室信息管理系统LIMS源码

技术架构&#xff1a;ASP.NET dotnet 3.5 LIMS作为一个信息管理系统&#xff0c;它有着和ERP、MIS之类管理软件的共性&#xff0c;如它是通过现代管理模式与计算机管理信息系统支持企业或单位合理、系统地管理经营与生产&#xff0c;最大限度地发挥现有设备、资源、人、技术的…