Improved handling of structurally identical classes and instanceof expressions

TypeScript 2.7 improves the handling of structurally identical classes in union types and instanceof expressions:

  • Structurally identical, but distinct, class types are now preserved in union types (instead of eliminating all but one).
  • Union type subtype reduction only removes a class type if it is a subclass of and derives from another class type in the union.
  • Type checking of the instanceof operator is now based on whether the type of the left operand derives from the type indicated by the right operand (as opposed to a structural subtype check).This means that union types and instanceof properly distinguish between structurally identical classes.

Example:

  1. class A {}
  2. class B extends A {}
  3. class C extends A {}
  4. class D extends A { c: string }
  5. class E extends D {}
  6. let x1 = !true ? new A() : new B(); // A
  7. let x2 = !true ? new B() : new C(); // B | C (previously B)
  8. let x3 = !true ? new C() : new D(); // C | D (previously C)
  9. let a1 = [new A(), new B(), new C(), new D(), new E()]; // A[]
  10. let a2 = [new B(), new C(), new D(), new E()]; // (B | C | D)[] (previously B[])
  11. function f1(x: B | C | D) {
  12. if (x instanceof B) {
  13. x; // B (previously B | D)
  14. }
  15. else if (x instanceof C) {
  16. x; // C
  17. }
  18. else {
  19. x; // D (previously never)
  20. }
  21. }