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, 为什么 anystring 是相同的类型,为什么 readonly 不能被区分出来?

  • tsany 可以赋给任何类型,任何类型也都可以赋给 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 实现的,如果有知道的大佬,请不吝赐教

原文地址

https://juejin.cn/post/7001857261756743694

❤️ 看完三件事

非常棒的一篇Ts实用文章,

如果你觉得这篇内容对你挺有启发,不妨:

  • 点个【在看】,或者分享转发,让更多的人也能看到这篇内容

  • 点击↓面关注我们,一起学前端

  • 长按↓面二维码,添加鬼哥微信,一起学前端



浏览 117
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报