TS 中实现 IsEqual 工具
共 3605字,需浏览 8分钟
·
2021-09-05 00:51
最近刷体操题,很多场景都需要判断两个类型是否相同,那么如何优雅地实现isEuqal<A, B>
这样一个工具类 呢?
很自然地想到了使用 extends
,两个类型互相 extends
的话那不就表示类型相同了么?
于是就有了第一种实现
原始方法
type IsEqual<A, B> = A extends B ? B extends A ? true : false : false
看起来是不是很容易?让我们找几个🌰测试一下
type IsEqual<A, B> = A extends B ? B extends A ? true : false : false
type A1 = IsEqual<string, string> // true
type A2 = IsEqual<string, number> // false
type A3 = IsEqual<{ name: string }, { name: string }> // true
type A4 = IsEqual<{ name: string}, { age: number }> // false
type A5 = IsEqual<{ name: string}, { name?: string }> // false
看起来效果也很好,那是不是这个实现就是完美了呢?
让我们再看看其他的🌰
type A6 = IsEqual<true, boolean> // boolean
type A7 = IsEqual<1 | 2, 1> // boolean
对于这两个🌰,我们期望得到的结果都是 false
,但是实际结果却都是 boolean
,这就有点奇怪了,明明返回的结果应该是 true
or false
,怎么返回了一个 boolean
?
事实上,boolean
类型在 ts
中和 true | false
是等价的,所以返回的类型是 boolean
其实就是返回了 true | false
,也就是在 IsEqual
这个工具类里面两个分支都走了。而为什么会走两个分支呢?这又是因为泛型和 extends
两者结合产生的 distributive conditional types
这样一种效应导致的。
既然第一种实现不太完美,那我们就该对症下药去解决第一个缺陷的地方,因为 extends
和泛型是这个实现里面必备的要素,但是又想避免由于这两者结合所带来的的 distributive conditional types
造成的影响。如何解决这种影响呢?也就是如何去消除 distributive conditional types
呢?ts
官方告诉我们,给泛型套上一个[]
就可以消除这种效应
于是,我们有了第二种实现
削微改进
type IsEqual<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false
当然,如果你嫌这样的实现太丑陋的话可以简化一下
type IsEqual<A, B> = [A, B] extends [B, A] ? true : false
这两种实现是等价的
让我们再去试试刚刚没有通过的两个🌰
type A7 = IsEqual<true, boolean> // false
type A8 = IsEqual<1 | 2, 1> // false
我们会发现这一回这俩🌰都可以完美通过了,是不是大功告成了呢?
(其实这个实现已经可以帮助我们解决很大一部分的问题了)
但是很遗憾,还有些顽固🌰拒绝通过
type A9 = IsEqual<any, string> // true
type A10 = IsEqual< { name: string }, { readonly name: string }> // true
unbelivable, 为什么 any
和 string
是相同的类型,为什么 readonly
不能被区分出来?
在 ts
中any
可以赋给任何类型,任何类型也都可以赋给any
,这就意味着any
和任意类型之间都是assignable
的,对于extends
而言就是都可以互相extends
,这也就是为什么A9
类型是true
readonly
不会改变assignable
对于以上这两种顽固情况该怎么解决呢
经过一通 Google,终于找到了一个相对完美的实现
Final version
type IsEqual<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T1>() => T1 extends B ? 1 : 2) ? true : false
具体可参考 github.com/microsoft/T…[1]
再来看一下刚刚的顽固🌰
type A9 = IsEqual<any, string> // false
type A10 = IsEqual< { name: string }, { readonly name: string }> // false
最终版可以帮助你解决 Get Readonly Keys[2]
结语:
其实最终版的实现我还不是很懂,只知道是使用了 ts
内部的 isTypeIdenticalTo
实现的,如果有知道的大佬,请不吝赐教
原文地址
❤️ 看完三件事
非常棒的一篇Ts实用文章,
如果你觉得这篇内容对你挺有启发,不妨:
点个【在看】,或者分享转发,让更多的人也能看到这篇内容
点击↓面关注我们,一起学前端
长按↓面二维码,添加鬼哥微信,一起学前端