大家好,欢迎来到IT知识分享网。
![TS 判断类型 “相等” 并不简单!插图1 TS 判断类型 “相等” 并不简单!](https://yundeesoft.com/wp-content/uploads/2022/11/2022112316405970.jpg)
TypeScript 中的类型相等
如果我们想判断两个变量是否相等,可以简单的通过 == 或 === 来进行比较,但是对比两个类型则不行。
在 TypeScript 中,类型是静态的,只会在编译时进行类型检查。
如果我们有两个类型 A 和 B,我们直接比较两个类型是否相等则会报错:
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[1] 中一位大神提到的 :
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 的可赋值性[2] 简单总结一下:
- 如果两个类型相等,那么它们可以相互赋值。
- 判断简单类型的可赋值性就是判断它们是否相等。
- 对于对象类型的可赋值性,如果一个类型是另一个类型的超类型(即包含了另一个类型的所有成员),那么这个类型的值可以赋给另一个类型的变量。
var source: { a: number, b: string }; var target: { a: number }; target = source;
- 函数类型的可赋值性是一个比较复杂的概念,需要考虑函数的参数类型和返回类型。在判断返回值时,需要确保原函数的返回值可以赋值给目标函数的返回值;而在比较参数时,则需要以逆变的方式进行,即确保目标函数的参数可以赋值给原函数的参数。举个例子来说明可能会更容易理解:
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 函数[3]。
现在我们分析类型相等的代码:
(<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 函数:
![TS 判断类型 “相等” 并不简单!插图3 TS 判断类型 “相等” 并不简单!](https://yundeesoft.com/wp-content/uploads/2022/11/2022112316405970.jpg)
这个函数用于检查源类型 source 是否与目标类型 target 相关,其中关系 relation 可以是 identityRelation(恒等关系),subtypeRelation(子类型关系),assignableRelation(可赋值关系),或 comparableRelation(可比较关系)。
然后在其中找到了关于条件类型的判断代码链接[4]:
![TS 判断类型 “相等” 并不简单!插图5 TS 判断类型 “相等” 并不简单!](https://yundeesoft.com/wp-content/uploads/2022/11/2022112316405970.jpg)
根据注释,如果有两个条件类型 T1 extends U1 ? X1 : Y1 和 T2 extends U2 ? X2 : Y2,它们被认为是相关的,需要满足以下条件:
- T1 和 T2 中的一个与另一个相关。这意味着 T1 可以赋值给 T2 或者 T2 可以赋值给 T1。
- U1 和 U2 是相同的类型。
- X1 可以赋值 X2。
- Y1 可以赋值 Y2。
对于源代码中的泛型 T 我们对其都没有任何约束,我理解他们两个是具有可赋值性的(这里我并不确定,仅主观猜测)。
接下来,我们再来看一下 IsEqual 代码:
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
此时我们可以发现,当 X 和 Y 相等,(<T>() => T extends X ? 1 : 2) 可以赋值给 (<T>() => T extends Y ? 1 : 2) ,所以 IsEqual 为 true,反之则为 false。
小结
TypeScript 类型越研究越发现有非常多的内容,包括本文讨论的 Equal 其实已经涉及到编译器相关源码,能够提出这个函数的人肯定是对 TS 非常了解才能举重若轻地写出这个有些 hack 的代码,不过还是希望 TS 官方早日能给出 Equal 函数,减轻大家的心智负担。:)
参考资料
- [Feature request] type level equal operator[5]
- Conditional Types[6]
- www.zhihu.com/question/57…[7]
Reference
[1] https://github.com/microsoft/TypeScript/issues/27024#issuecomment-: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fmicrosoft%2FTypeScript%2Fissues%2F27024%23issuecomment-
[2] https://juejin.cn/post/: https://juejin.cn/post/
[3] https://github1s.com/microsoft/TypeScript/blob/f1ff0de94326b554f46a71941ca453fb2559a752/src/compiler/checker.ts#L19765-L19766: https://link.juejin.cn?target=https%3A%2F%2Fgithub1s.com%2Fmicrosoft%2FTypeScript%2Fblob%2Ff1ff0de94326b554f46a71941ca453fb2559a752%2Fsrc%2Fcompiler%2Fchecker.ts%23L19765-L19766
[4] https://github1s.com/microsoft/TypeScript/blob/f1ff0de94326b554f46a71941ca453fb2559a752/src/compiler/checker.ts#L21165-L21166: https://link.juejin.cn?target=https%3A%2F%2Fgithub1s.com%2Fmicrosoft%2FTypeScript%2Fblob%2Ff1ff0de94326b554f46a71941ca453fb2559a752%2Fsrc%2Fcompiler%2Fchecker.ts%23L21165-L21166
[5] https://github.com/microsoft/TypeScript/issues/27024#issuecomment-: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fmicrosoft%2FTypeScript%2Fissues%2F27024%23issuecomment-
[6] https://www.typescriptlang.org/docs/handbook/2/conditional-types.html: https://link.juejin.cn?target=https%3A%2F%2Fwww.typescriptlang.org%2Fdocs%2Fhandbook%2F2%2Fconditional-types.html
[7] https://www.zhihu.com/question/: https://link.juejin.cn?target=https%3A%2F%2Fwww.zhihu.com%2Fquestion%2F
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/74465.html