你知道在 TS 中判断两个类型相等有多难吗?

2024-05-14 02:52

本文主要是介绍你知道在 TS 中判断两个类型相等有多难吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

公众号:程序员白特,欢迎一起交流学习~

TypeScript 中的类型相等

如果我们想判断两个变量是否相等,可以简单的通过 ===== 来进行比较,但是对比两个类型则不行。

在 TypeScript 中,类型是静态的,只会在编译时进行类型检查。

如果我们有两个类型 AB,我们直接比较两个类型是否相等则会报错:

type A = number;
type B = string;
type C = number == string; // 'string' only refers to a type, but is being used as a value here.ts(2693)

我们可以看到 TypeScript 提醒我们不能把类型当做值来用。

那我们如何判断两个类型是否相等呢?用的比较广泛的是 GitHub [Feature request]type level equal operator 中一位大神提到的 :

export type Equals<X, Y> =(<T>() => T extends X ? 1 : 2) extends(<T>() => T extends Y ? 1 : 2) ? true : false;

这段代码虽然很好用,但是原理却让人一头雾水,所以我尝试来分析下它到底是如何运作的。

条件类型

不了解Typescript类型的人可能对T extends X ? 1 : 2的含义不太了解,实际上这是TS中的条件类型。

在Typescript中,有一个特性叫“条件类型(Conditional Types)”,条件类型的形式有点像JavaScript中的条件表达式(condition ? trueExpression : falseExpression):.

SomeType extends OtherType ? TrueType : FalseType;

当 extends 左边的类型可以赋值给右边的类型时,我们会得到第一个分支的类型 TrueType,否则得到后面的类型 FalseType

举个例子:

interface Animal {live(): void;
}
interface Dog extends Animal {woof(): void;
}
type Example1 = Dog extends Animal ? number : string; // number
type Example2 = RegExp extends Animal ? number : string; // string

那么接下来的问题是,既然条件类型判断的是一个类型是否能复制给另一个类型,那么如何确定是否可以赋值呢?我们需要深入了解一下Typescript的可赋值性。

Typescript 的可赋值性

为了了解 Typescript 的可赋值性,我专门找了一篇文章,并翻译了一下:[译]TypeScript 的可赋值性 简单总结一下:

  1. 如果两个类型相等,那么它们可以相互赋值。

  2. 判断简单类型的可赋值性就是判断它们是否相等。

  3. 对于对象类型的可赋值性,如果一个类型是另一个类型的超类型(即包含了另一个类型的所有成员),那么这个类型的值可以赋给另一个类型的变量。

    var source: { a: number, b: string };
    var target: { a: number };
    target = source;
    
  4. 函数类型的可赋值性是一个比较复杂的概念,需要考虑函数的参数类型和返回类型。在判断返回值时,需要确保原函数的返回值可以赋值给目标函数的返回值;而在比较参数时,则需要以逆变的方式进行,即确保目标函数的参数可以赋值给原函数的参数。举个例子来说明可能会更容易理解:

    var source: (a: string) => void;
    var target: (a: unknown) => void;
    target = source; // should be an error, because:
    target(1); // oops, can't pass numbers to source
    

这篇文章还有好多内容还没有讲,作者最后说 可以在 GitHub 上的 TypeScript 仓库中的 src/compiler/checker.ts 文件中查看 checkTypeRelatedTo 函数。

现在我们分析类型相等的代码:

(<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)? true : false;

可以观察到,需要判断两个函数类型的可赋值性:(<T>() => T extends X ? 1 : 2) 是否可以赋值给 extends (<T>() => T extends Y ? 1 : 2)。这两个函数没有参数,我们只需要考虑返回值是否可以赋值。也就是说,条件类型 T extends X ? 1 : 2 是否可以赋值给条件类型 T extends Y ? 1 : 2

条件类型的可赋值性

如何判断一个条件类型可以赋值给另一个条件类型呢?在 Typescript 的仓库中 src/compiler/checker.ts 我们找到了 checkTypeRelatedTo 函数:

这个函数用于检查源类型 source 是否与目标类型 target 相关,其中关系 relation 可以是 identityRelation(恒等关系),subtypeRelation(子类型关系),assignableRelation(可赋值关系),或 comparableRelation(可比较关系)。

然后在其中找到了关于条件类型的判断代码链接:

根据注释,如果有两个条件类型 T1 extends U1 ? X1 : Y1T2 extends U2 ? X2 : Y2,它们被认为是相关的,需要满足以下条件:

  1. T1T2 中的一个与另一个相关。这意味着 T1 可以赋值给 T2 或者 T2 可以赋值给 T1
  2. U1U2 是相同的类型。
  3. X1 可以赋值 X2
  4. Y1 可以赋值 Y2

对于源代码中的泛型 T 我们对其都没有任何约束,我理解他们两个是具有可赋值性的(这里我并不确定,仅主观猜测)。

接下来,我们再来看一下 IsEqual 代码:

type IsEqual<X, Y> =(<T>() => T extends X ? 1 : 2) extends(<T>() => T extends Y ? 1 : 2) ? true : false;

此时我们可以发现,当 XY 相等,(<T>() => T extends X ? 1 : 2) 可以赋值给 (<T>() => T extends Y ? 1 : 2) ,所以 IsEqualtrue,反之则为 false

小结

TypeScript 类型越研究越发现有非常多的内容,包括本文讨论的 Equal 其实已经涉及到编译器相关源码,能够提出这个函数的人肯定是对 TS 非常了解才能举重若轻地写出这个有些 hack 的代码,不过还是希望 TS 官方早日能给出 Equal 函数,减轻大家的心智负担。😃

参考资料

  • [Feature request] type level equal operator
  • Conditional Types
  • www.zhihu.com/question/57…

这篇关于你知道在 TS 中判断两个类型相等有多难吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/987549

相关文章

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

Pydantic中Optional 和Union类型的使用

《Pydantic中Optional和Union类型的使用》本文主要介绍了Pydantic中Optional和Union类型的使用,这两者在处理可选字段和多类型字段时尤为重要,文中通过示例代码介绍的... 目录简介Optional 类型Union 类型Optional 和 Union 的组合总结简介Pyd

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

Python如何查看数据的类型

《Python如何查看数据的类型》:本文主要介绍Python如何查看数据的类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python查看数据的类型1. 使用 type()2. 使用 isinstance()3. 检查对象的 __class__ 属性4.

Python容器类型之列表/字典/元组/集合方式

《Python容器类型之列表/字典/元组/集合方式》:本文主要介绍Python容器类型之列表/字典/元组/集合方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 列表(List) - 有序可变序列1.1 基本特性1.2 核心操作1.3 应用场景2. 字典(D

Python如何在Word中生成多种不同类型的图表

《Python如何在Word中生成多种不同类型的图表》Word文档中插入图表不仅能直观呈现数据,还能提升文档的可读性和专业性,本文将介绍如何使用Python在Word文档中创建和自定义各种图表,需要的... 目录在Word中创建柱形图在Word中创建条形图在Word中创建折线图在Word中创建饼图在Word

SpringBoot接收JSON类型的参数方式

《SpringBoot接收JSON类型的参数方式》:本文主要介绍SpringBoot接收JSON类型的参数方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、jsON二、代码准备三、Apifox操作总结一、JSON在学习前端技术时,我们有讲到过JSON,而在