如何在Typescript中检查类型相等?

3

在寻找如何在TypeScript中测试类型相等性时,我发现了这个SO问题,它的标题看起来很有前途,但实际上问题并不是关于测试类型相等性的。

我想要一个可以测试两种类型等价性的东西:

type IsEqual<Type1, Type2> = ???;

比如,它可以用来确定重载函数的返回类型:

type IsAmbiguous<T> = T extends { isString: infer K } ? IsEqual<K,boolean> extends true ? T : never : never;

function doThing(val: number,    opts: { isString: true }): string;
function doThing(val: number,    opts: { isString: false }): number;
function doThing<T>(val: number, opts: IsAmbiguous<T>): string | number;

前两个重载函数从传入选项的狭窄类型中预测返回类型。而在第三个重载函数中,opts 可以通过变量传递,此时 isString 的类型为布尔值,并且返回类型将由 doThing 实现在运行时确定,但是 TypeScript 编译器可能无法在编译时知道(或不知道)。

IsEqual 的实现是什么?

1个回答

2

回答自己的问题。

type IsEqual<Type1,Type2> = (Type1 | Type2) extends (Type1 & Type2) ? true : never;

测试类型相等的关键是使用交集 (&) 和并集 (|) 运算符。我们测试两种类型的并集是否扩展了两种类型的交集。在 TypeScript (和 JavaScript) 中,subtype extends supertype 意味着 subtype 是一个更具体的类型,而 supertype 是一个更一般的类型。对于对象和类,这通常意味着向派生类型添加属性或方法。对于标量类型,这意味着子类型是一个特定的值 (3.14 extends number)。

在这里,IsEqual 接受一个更一般的类型 (union),并测试它是否扩展了一个更窄的类型 (intersection)。如果它确实扩展了,则两种类型必须相等,因为没有任何额外的内容被任何一种类型添加进去。如果引入了一些额外的类型,则超类型将比子类型更窄,因此不能扩展。

以下是一些狭窄和广泛扩展的示例。

var goo: string | number extends string ? true : false                  = false;
var boo: true extends boolean ? true : false                            = true;
var too: { x: number } extends { x: number, y: number } ? true : false  = false;
var woo: { x: number, y: number } extends { x: number } ? true : false  = true;

这里有一个TS Playground,其中包含IsEqual的实现和使用示例。

在准备这个问答过程中,我发现这篇 Medium 文章这个 Stack Overflow 回答非常有帮助。

type result = IsEqual<{ readonly a: 'A' }, { a: 'A' }>; 这里的result是true,这是你的意图吗? - undefined
@annes谢谢你的评论。虽然这不是我的意图,但我不确定这是否重要,因为这个问题已被关闭为重复,并且链接的答案有一个更好的计算IsEqual的方法。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接