首页 > 解决方案 > 子类型与其超类型的交集是否与子类型相同?

问题描述

给定以下 TypeScript 代码:

interface A { /* ... */ }
interface B extends A { /* ... */ }

type Mystery = A & B;

该类型的对象Mystery应该具有的所有属性A和所有属性,B因为它是一个交集类型。类型的对象B应该已经具有Abecause的所有属性B extends A

Mystery鉴于这些定义, type和 type之间有区别B吗?

标签: typescriptset-intersection

解决方案


对于第一个近似值,如果B extends A,那么A & BB是相同的。编译器甚至会考虑A & BB相互分配:

function foo<A, B extends A>(u: B, m: A & B) {
  u = m; // okay
  m = u; // okay
}

虽然不相同:

function foo<A, B extends A>(u: B, m: A & B) {    
  var v: B;
  var v: A & B; // error, not considered identical
}

在实践中,会出现编译器处理不同A & B的情况。B其中一些是被视为错误或设计限制的编译器实现细节;我得去挖掘这些。

但是一个特定的地方可能很容易不同,这与调用签名的交集如何被解释为A & B可以以多种方式调用的重载函数有关,而具有调用签名的接口扩展往往只是覆盖父接口调用签名并且可以只能被称为一种方式。例如:B

interface A { method(p: any): void }
interface B extends A { method(): void }

这是允许的,因为参数较少的函数可以分配给参数较多的函数。该类型B只看到零参数方法,因此您会得到以下行为:

declare const a: A;
a.method(0); // okay
declare const b: B;
b.method(); // okay
b.method(0); // error!

由于method()在 中被覆盖Bb.method()因此没有参数是错误的(即使零参数方法可分配给多参数方法,您仍然不能故意调用具有太多参数的函数而没有警告)。

将此与交叉点进行比较:

type Mystery = A & B;
declare const m: Mystery;
m.method(""); // okay
m.method(); // okay

如果你检查m.method,你会看到它有两个重载:

// 1/2 method(p: any): void 
// 2/2 method(): void

因此可以通过两种方式中的任何一种调用。


Playground 代码链接


推荐阅读