TS:子类型关系

子类型关系

  • 1、概念
    • 1.1 里氏替换原则
    • 1.2 自反性
    • 1.3 传递性
  • 2、顶端类型 和 尾端类型
  • 3、字面量类型
  • 4、undefined 和 null
  • 5、枚举类型
  • 6、函数类型
    • 6.1 变型
      • 6.1.1 协变
      • 6.1.2 逆变
      • 6.1.3 双变
    • 6.2 函数类型间的子类型关系
      • 6.2.1 函数参数数量
      • 6.2.2 函数参数类型
        • A、非严格函数类型检查
        • B、严格函数类型检查
      • 6.2.3 函数返回值类型
      • 6.2.4 函数重载
  • 7、对象类型
    • 7.1 属性成员类型
    • 7.2 调用签名 和 构造签名
    • 7.3 字符串索引签名 和 数值型索引签名
  • 8、类类型
  • 9、泛型类型
    • 9.1 泛型对象类型
    • 9.2 泛型函数类型
      • 9.2.1 非严格泛型函数类型检查
      • 9.2.2 严格泛型函数类型检查
  • 10、联合类型 和 交叉类型
    • 10.1 联合类型
    • 10.2 交叉类型

1、概念

1.1 里氏替换原则

程序中任何使用了超类型 的地方都可以用其 子类型 进行替换,并且在替换后程序的行为保持不变。

1.2 自反性

任意类型都是其 自身 的子类型和超类型。

1.3 传递性

若 类型A 是 类型B 的子类型,且 类型B 是 类型C 的子类型,那么 类型A 也是 类型C 的子类型。

2、顶端类型 和 尾端类型

顶端类型是一种通用超类型,所有类型都是顶端类型的子类型;同时,尾端类型是所有类型的子类型。

TS 中存在两种顶端类型,即any类型和unknown类型。因此,所有类型都是any类型和unknown类型的子类型。

3、字面量类型

字面量类型是其对应的基础原始类型的子类型。

例如:maintainer 类型是 name 类型的子类型

type maintainer = 'Bob';
type name = 'string';

4、undefined 和 null

Undefined类型是 除尾端类型 never 外 所有类型的子类型,其中也包括null类型。
Null类型是 除尾端类型和undefined类型外 的所有类型的子类型。

5、枚举类型

在联合枚举类型中,每个枚举成员都能够表示一种类型,同时联合枚举成员类型是联合枚举类型的子类型。

什么是联合枚举类型?
枚举成员类型都是字面量类型的枚举类型。

6、函数类型

函数类型由 参数类型返回值类型 构成。在比较两个函数类型间的子类型关系时要同时考虑 参数类型返回值类型

6.1 变型

在学习函数类型的字类型和超类型之间的关系前,我们要先了解一个和函数类型关系非常紧密的概念——变型

所谓变型就是描述的是复杂类型的组成类型是如何影响复杂类型间的子类型关系的。

现约定如果复杂类型 Complex 是由 类型T 构成,那么我们将其记作 Complex(T)。

6.1.1 协变

假设有两个复杂类型 Complex(A) 和 Complex(B) ,如果由A是B的子类型能够得出 Complex(A) 是 Complex(B) 的子类型,那么我们将这种变型称作协变。

协变关系维持了复杂类型与其组成类型间的子类型关系。

6.1.2 逆变

如果由A是B的子类型能够得出 Complex(B) 是 Complex(A) 的子类型,那么我们将这种变型称作逆变。

6.1.3 双变

如果由 A 是 B 的子类型或者 B 是 A 的子类型能够得出 Complex(A) 是 Complex(B) 的子类型,那么我们将这种变型称作双变。

双变同时具有协变关系与逆变关系。

6.2 函数类型间的子类型关系

6.2.1 函数参数数量

子类型的 必选参数 的个数 不能多于 父类型 中的参数个数

若函数类型 S 是函数类型 T 的子类型,则 S 中的每一个必选参数必须能够在T中找到对应的参数。

当T中存在 可选参数剩余参数 时,函数类型检查是不可靠的。因为当使用子类型 S 替换了超类型 T 之后,调用 S 时的实际参数个数可能少于必选参数的个数。例如,有如下的函数s 和函数t ,其中 s 是 t 的子类型,使用一个实际参数调用函数 t 没有问题,但是将 t 替换为其子类型 s 后会产生错误,因为调用 s 需要两个实际参数。

function s(a: number, b: number): void {}
function t(...x:number[]): void {}t(0);
s(0); // 编译错误

6.2.2 函数参数类型

函数的参数类型会影响函数类型间的子类型关系。

A、非严格函数类型检查

在该模式下,函数参数类型与函数类型是 双变 关系。

若函数类型 S 是函数类型 T 的子类型,那么 S 的参数类型必须是 T 中对应参数类型的【子类型】或者【超类型】。这意味着在对应位置上的两个参数只要存在子类型关系即可,而不强调哪一方应该是另一方的子类型。

type S = (a: 0 | 1) => void;
type T = (x: number) => void;

此例中,S 是 T 的子类型,同时 T 也是 S 的子类型。

B、严格函数类型检查

strictFunctionTypes 编译选项用来启用严格的函数类型检查。

在该模式下,函数参数类型与函数类型是 逆变 关系,而非相对宽松的双变关系。

若函数类型 S 是函数类型 T 的子类型,那么 S 的参数类型必须是 T 中对应参数类型的 超类型

6.2.3 函数返回值类型

在确定函数类型间的子类型关系时,编译器将检查函数返回值类型 是否兼容

不论是否启用了strictFunctionTypes 编译选项,函数返回值类型与函数类型始终是 协变 关系。

若函数类型 S 是函数类型 T 的子类型,那么 S 的返回值类型必须是 T 的返回值类型的 子类型

我理解,无论是函数返回值类型还是严格模式下的函数参数类型检查,都要满足赋值兼容性,才符合里氏替换原则。
对于严格模式下的参数类型,超类型函数的实参要能赋值给子类型的形参。
对于函数返回值类型,子类型函数的返回值要能赋值给超类型函数的返回值。

6.2.4 函数重载

在确定函数类型间的子类型关系时,编译器将检查函数重载签名类型是否兼容。

若函数类型 S 是函数类型 T 的子类型,并且 T 存在函数重载,那么 T 的每一个函数重载必须能够在 S 的函数重载中找到与其对应的子类型。

7、对象类型

在比较对象类型的子类型关系时要分别考虑 每一个 类型成员。

在结构化子类型系统中仅通过比较两个对象类型的 类型成员列表 就能够确定它们的子类型关系。对象类型的名称完全不影响对象类型间的子类型关系。

例如,以下示例中 类型 A 和 类型 B 是相同类型,类型 C 是 类型 A 和 类型 B 的超类型。

type A = {name: string,age: number
}
type B = {name: string,age: number
}
type C = {name: string
}

对象类型的子类型关系要满足以下条件:

  1. 数量上,超类型对象类型的成员数量不能多于 子类型对象类型的成员数量。
  2. 属性成员的可访问性上,超类型中的必选属性在子类型中也必须是必选属性。
  3. 成员类型上,子类型的成员类型是超类型中对应的成员类型的子类型。

7.1 属性成员类型

超类型的每个属性成员 M 都能够在子类型中找到同名属性成员 N,且 N 是 M 的子类型。

7.2 调用签名 和 构造签名

超类型中的调用签名 M 都能够在子类型中找到对应的调用签名 N,且 N 是 M 的子类型。

对象类型中的构造签名与调用签名有着相同的判断规则。

7.3 字符串索引签名 和 数值型索引签名

父类型中有索引签名,子类型中也要有,并子类型的索引签名类型是父类型索引签名类型的子类型。

8、类类型

在确定两个类类型之间的子类型关系时,仅检查类的 实例成员 类型,类的 静态成员类型 以及 构造函数类型 不进行检查。

如果类中存在 私有成员受保护成员,那么在确定类类型间的子类型关系时要求私有成员和受保护成员 来自同一个类,这意味着两个类需要存在 继承关系

例如,以下代码中,Point 和 Position 中的受保护成员 x 都来自Point,因此 Position 是 Point 的子类型。

class Point {protected x: number = 0;
}
class position extends Point {protected y: number = 0;
}

9、泛型类型

9.1 泛型对象类型

对于泛型接口、泛型类和表示对象类型的泛型类型别名而言,实例化泛型类型时使用的实际类型参数 不影响 子类型关系,真正影响子类型关系的是泛型实例化后的 结果对象类型

9.2 泛型函数类型

TS编译器提供了noStrictGenericChecks 编译选项用来启用或关闭严格泛型函数类型检查。

9.2.1 非严格泛型函数类型检查

编译器先将所有的泛型类型参数替换为 any 类型,然后再确定子类型关系。这意味着泛型类型参数不影响泛型函数的子类型关系。

type A = <T, U>(x: T, y: U) => [T, U];
type B = <S>(x: S, y: S) => [S, S];

将所有的类型参数替换为any类型,结果如下:

type A = (x: any, y: any) => [any, any];
type B = (x: any, y: any) => [any, any];

9.2.2 严格泛型函数类型检查

先通过类型推断来 统一 两个泛型函数的类型参数,然后再确定两者的子类型关系。

type A = <T, U>(x: T, y: U) => [T, U];
type B = <S>(x: S, y: S) => [S, S];

如果我们想要确定 A 是否为 B 的子类型,那么先尝试使用 B 的类型来推断A的类型。通过比较每个参数类型和返回值类型,能够得出类型参数 T 和 U 均为 S。接下来使用推断的结果来实例化 A 类型,即将类型 A 中的 T 和 U 均替换为 S,替换后的结果如下:

type A = <S>(x: S, y: S) => [S, S];
type B = <S>(x: S, y: S) => [S, S];

在统一了类型参数之后,再来比较泛型函数间的子类型关系。因为统一后的类型 A 和 B 相同,所以 A 是 B 的子类型。

如果我们最开始想要确定 B 是否为 A 的子类型,那么这时将由 A 向 B 来推断并统一类型参数的值。经推断,S 的类型为联合类型“T | U”,然后使用“S = T | U”来实例化 B 类型,结果如下:

type A = <T, U>(x: T, y: U) => [T, U];
type B = <T, U>(x: T | U, y: T | U) => [T | U, T | U];

此时,B 不是 A 的子类型,因为 B 的返回值类型不是 A 的返回值类型的子类型。

10、联合类型 和 交叉类型

10.1 联合类型

联合类型由若干成员类型构成,在计算联合类型的子类型关系时需要考虑每一个成员类型。

假设有联合类型 S = S0 | S1 和任意类型 T,如果成员类型 S0 是类型 T 的子类型,并且成员类型 S1 是类型 T 的子类型,那么联合类型 S 是类型 T 的子类型。例如,有如下定义的联合类型 S 和类型 T 。

type S = 0 | 1;
type T = number;

此例中,联合类型 S 是类型 T 的子类型。

假设有联合类型 S = S0 | S1 和任意类型 T,如果类型 T 是成员类型 S0 的子类型,或者类型T是成员类型 S1 的子类型,那么类型 T 是联合类型 S 的子类型。例如,有如下定义的联合类型 S 和类型 T :

type S = number | string;
type T = 0;

此例中,类型T是联合类型S的子类型。

10.2 交叉类型

交叉类型由若干成员类型构成,在计算交叉类型的子类型关系时需要考虑每一个成员类型。

假设有交叉类型 S = S0 & S1 和任意类型 T,如果成员类型 S0 是类型 T 的子类型,或者成员类型 S1 是类型 T 的子类型,那么交叉类型 S 是类型 T 的子类型。

type S = { x: number } & { y: number };
type T = { x: number };

假设有交叉类型 S = S0 & S1 和任意类型 T,如果类型 T 是成员类型 S0 的子类型,并且类型 T 是成员类型 S1 的子类型,那么类型 T 是交叉类型 S 的子类型。

type S = { x: number } & { y: number };
type T = { x: number; y: number; z: number };

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

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

相关文章

React实现组件扩展机制

在java中&#xff0c;SPI机制是Java中提供的一种服务发现机制。同样&#xff0c;前端也很需要这种机制&#xff0c;这样可以做到组件可插拔&#xff0c;可替换&#xff0c;减少相互冗余。 快速使用 1.扩展点使用 通过使用Extension组件定义扩展点&#xff0c;通过name标记扩展…

2023爱分析·知识库问答市场厂商评估报告:爱数

01 研究范围定义 研究范围&#xff1a; 大模型是指通过在海量数据上依托强大算力资源进行训练后能完成大量不同下游任务的模型。2023年以来&#xff0c;ChatGPT引爆全球大模型市场。国内众多大模型先后公测&#xff0c;众多互联网领军者投身大模型事业&#xff0c;使得大模型…

python验证服务器或容器端口是否可以用

背景 我们在验证服务器的某个端口是否可用&#xff0c;除了使用netstat&#xff0c;lsof命令来查看&#xff0c;如果端 口是在远程服务器上&#xff0c;就不用登录到机器&#xff0c;可以通过socket连接来进行测试 原理实现&#xff1a; 就简单的一个socket连接验证 socket连接…

C++ //练习 3.39 编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容。

C Primer&#xff08;第5版&#xff09; 练习 3.39 练习 3.39 编写一段程序&#xff0c;比较两个string对象。再编写一段程序&#xff0c;比较两个C风格字符串的内容。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*******…

【SpringBoot】applicationContext.getBeansOfType(class)获取某一接口所有实现类,应用于策略模式

一、问题的提出 在实际工作中&#xff0c;我们经常会遇到一个接口及多个实现类的情况&#xff0c;并且在不同的条件下会使用不同的实现类。 二、应用场景 springboot 项目中通过 ApplicationContext.getBeansOfType(class) 获取某一接口的所有实现类&#xff0c;并通过枚举完…

mybatis面试

1.说一下对Mybatis的理解 1.1 mybatis概念 mybatis是一个半自动的持久层ORM框架 1.2 什么是ORM Object Relational Mapping【对象 关系 映射】&#xff0c;将Java中的对象与数据库中表建议映射关系 1.3 Mybatis与Hibernate对比 Mybatis是一个半自动化&#xff0c;需要手写…

Java多线程文档与入门-Thread与Runnable

一.Thread Fields Modifier and TypeField and Descriptionstatic intMAX_PRIORITY 线程可以拥有的最大优先级。 static intMIN_PRIORITY 线程可以拥有的最小优先级。 static intNORM_PRIORITY 分配给线程的默认优先级。 构造方法 Constructor and DescriptionThread() 分配一…

每日一道Java面试题:说一说Java中的异常

写在开头 任何一个程序都无法保证100%的正常运行&#xff0c;程序发生故障的场景&#xff0c;我们称之为&#xff1a;异常&#xff0c;在Java中对于异常的处理有一套完善的体系&#xff0c;今天我们就来一起学习一下。老样子&#xff0c;用一段简单的代码开始今天的学习。 我&a…

【CanvasKeyFrames - HTML5 Canvas 图片序列帧播放工具】

前言 一、CanvasKeyFrames 是什么&#xff1f; 用来做canvas动画的工具。 二、使用步骤 效果如图&#xff1a;上下波动的线条 1.引入库 代码如下&#xff08;示例&#xff09;&#xff1a; 在html中引入&#xff1a; <script src"canvas-keyframes.js"><…

中国34省市区名字之来源

本文内容是笔者根据B站视频进行整理,视频内容由@顺丰快递曹子桓 @小玄学投稿。 北京 洪武二年,朱元璋得知捷报,改“大都路”为“北平府”,取“蓟北悉平”之意。 朱棣改北平府为顺天府,又赐京号——北京。 民国时,顺天府先被改为京兆地方,又被改为北平市。 新中国成立后…

源聚达科技:开一家抖音小店有没有风险

在数字化浪潮的推动下&#xff0c;抖音小店如雨后春笋般涌现&#xff0c;成为众多创业者眼中的香饽饽。然而&#xff0c;“盛名之下&#xff0c;其实难副”&#xff0c;开设一家抖音小店并非只有风光无限&#xff0c;其背后的风险也不容小觑。 首要的风险源自激烈的市场竞争。抖…

力扣之2629.复合函数(reduceRight )

/*** param {Function[]} functions* return {Function}*/ var compose function(functions) {return function(x) {return functions.reduceRight((result, func) > func(result), x);} };/*** const fn compose([x > x 1, x > 2 * x])* fn(4) // 9*/ 说明&#x…

大模型ReAct智能体开发实战

哆啦A梦是很多人都熟悉的角色&#xff0c;包括我自己。 在成长过程中&#xff0c;我常常对他口袋里的许多小玩意感到惊讶&#xff0c;而且他知道何时使用它们。 随着大型语言模型 (LLM) 的发展趋势&#xff0c;你也可以构建一个具有相同行为方式的模型&#xff01; 我们将构建…

Redis是单线程还是多线程?

Redis最初是设计为单线程的服务器&#xff0c;其核心处理命令请求的逻辑是单线程的&#xff0c;这使得Redis非常简单而高效。核心单线程的设计意味着它使用非阻塞I/O&#xff0c;并且按顺序处理所有操作&#xff0c;从而避免了锁和多线程的竞争条件。 然而&#xff0c;在最近的…

DHCP简介

定义 动态主机配置协议DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;是一种用于集中对用户IP地址进行动态管理和配置的技术。即使规模较小的网络&#xff0c;通过DHCP也可以使后续增加网络设备变得简单快捷。 DHCP是在BOOTP&#xff08;BOOTstrap Protoc…

组装2-4人后端服务团队,选择GO还是Java?

问题&#xff1a; 组装2-4人后端服务团队&#xff0c;选择GO还是Java&#xff1f; 背景&#xff1a; 原团队python背景&#xff0c;现新业务需要用到大数据的处理&#xff0c;而python并不适合。 原业务的发展本想渐进发展中部分大数据业务改用GO实现逻辑部分&#xff0c;但新…

mysql数据库学习记录(一)

文章目录 1.mysql如何加外键关联约束&#xff1f;2.mysql&#xff0c;加外键关联约束有什么作用&#xff1f;3.在MySQL中&#xff0c;可以通过在创建数据表时使用PRIMARY KEY关键字来指定多列联合主键&#xff0c;那么请问联合主键有啥作用&#xff1f;mysql数据表设计好后&…

linux 04 进程管理

02.进程管理 ps 在命令行输入ps后按回车键就能查看当前系统中正在运行的进程。 第一. 查看进程ps 进程的状态STAT 进程的周期 fork&#xff0c;产生一个新进程 第二.排序进程表 ps aux --sort -%cpu 降序cpu %cpu 增序cpu 第三.父子关系 ps ef 第四.自定义 五.动态查看…

Spring和SpringBoot的区别是什么

Spring 和 Spring Boot 是 Java 开发领域内两个极其重要且紧密相关的框架&#xff0c;它们各自在企业级应用开发中扮演着不同的角色&#xff0c;并带来了一系列革新性的变化。以下是关于两者之间主要区别的详细分析&#xff1a; 一、设计理念与定位 Spring Framework Spring 是…

如何远程登录云服务器?登录失败是什么原因?

我用Linux云服务器&#xff0c;遇到了有关远程登录的一些问题&#xff0c;于是搜索了一些资料&#xff0c;整理了一篇文档&#xff0c;作为记录。如果你也遇到过 相似的问题&#xff0c;欢迎一起探讨&#xff01; 一、Linux云服务器的远程登录 远程登陆linux&#xff0c;使用…