Skip to content

Commit

Permalink
Merge pull request #10216 from Microsoft/structurallyIdenticalInstanceof
Browse files Browse the repository at this point in the history
Improve instanceof with structurally identical types
  • Loading branch information
ahejlsberg authored Aug 9, 2016
2 parents 3f6aa3f + fe1854e commit 3f1ec7a
Show file tree
Hide file tree
Showing 7 changed files with 664 additions and 13 deletions.
13 changes: 10 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5924,6 +5924,13 @@ namespace ts {
return isTypeRelatedTo(source, target, assignableRelation);
}

// A type S is considered to be an instance of a type T if S and T are the same type or if S is a
// subtype of T but not structurally identical to T. This specifically means that two distinct but
// structurally identical types (such as two classes) are not considered instances of each other.
function isTypeInstanceOf(source: Type, target: Type): boolean {
return source === target || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target);
}

/**
* This is *not* a bi-directional relationship.
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
Expand Down Expand Up @@ -8575,12 +8582,12 @@ namespace ts {

function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) {
if (!assumeTrue) {
return filterType(type, t => !isTypeSubtypeOf(t, candidate));
return filterType(type, t => !isTypeInstanceOf(t, candidate));
}
// If the current type is a union type, remove all constituents that aren't assignable to
// If the current type is a union type, remove all constituents that couldn't be instances of
// the candidate type. If one or more constituents remain, return a union of those.
if (type.flags & TypeFlags.Union) {
const assignableConstituents = filter((<UnionType>type).types, t => isTypeAssignableTo(t, candidate));
const assignableConstituents = filter((<UnionType>type).types, t => isTypeInstanceOf(t, candidate));
if (assignableConstituents.length) {
return getUnionType(assignableConstituents);
}
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/controlFlowBinaryOrExpression.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,19 @@ if (isNodeList(sourceObj)) {
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))

sourceObj.length;
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
}

if (isHTMLCollection(sourceObj)) {
>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))

sourceObj.length;
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
}

if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
Expand All @@ -86,8 +86,8 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))

sourceObj.length;
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
}

8 changes: 4 additions & 4 deletions tests/baselines/reference/controlFlowBinaryOrExpression.types
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ if (isNodeList(sourceObj)) {

sourceObj.length;
>sourceObj.length : number
>sourceObj : NodeList | HTMLCollection
>sourceObj : NodeList
>length : number
}

Expand All @@ -91,7 +91,7 @@ if (isHTMLCollection(sourceObj)) {

sourceObj.length;
>sourceObj.length : number
>sourceObj : NodeList | HTMLCollection
>sourceObj : HTMLCollection
>length : number
}

Expand All @@ -102,11 +102,11 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
>sourceObj : EventTargetLike
>isHTMLCollection(sourceObj) : boolean
>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection
>sourceObj : { a: string; }
>sourceObj : HTMLCollection | { a: string; }

sourceObj.length;
>sourceObj.length : number
>sourceObj : NodeList
>sourceObj : NodeList | HTMLCollection
>length : number
}

172 changes: 172 additions & 0 deletions tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//// [instanceofWithStructurallyIdenticalTypes.ts]
// Repro from #7271

class C1 { item: string }
class C2 { item: string[] }
class C3 { item: string }

function foo1(x: C1 | C2 | C3): string {
if (x instanceof C1) {
return x.item;
}
else if (x instanceof C2) {
return x.item[0];
}
else if (x instanceof C3) {
return x.item;
}
return "error";
}

function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 }
function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 }
function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 }

function foo2(x: C1 | C2 | C3): string {
if (isC1(x)) {
return x.item;
}
else if (isC2(x)) {
return x.item[0];
}
else if (isC3(x)) {
return x.item;
}
return "error";
}

// More tests

class A { a: string }
class A1 extends A { }
class A2 { a: string }
class B extends A { b: string }

function goo(x: A) {
if (x instanceof A) {
x; // A
}
else {
x; // never
}
if (x instanceof A1) {
x; // A1
}
else {
x; // A
}
if (x instanceof A2) {
x; // A2
}
else {
x; // A
}
if (x instanceof B) {
x; // B
}
else {
x; // A
}
}


//// [instanceofWithStructurallyIdenticalTypes.js]
// Repro from #7271
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var C1 = (function () {
function C1() {
}
return C1;
}());
var C2 = (function () {
function C2() {
}
return C2;
}());
var C3 = (function () {
function C3() {
}
return C3;
}());
function foo1(x) {
if (x instanceof C1) {
return x.item;
}
else if (x instanceof C2) {
return x.item[0];
}
else if (x instanceof C3) {
return x.item;
}
return "error";
}
function isC1(c) { return c instanceof C1; }
function isC2(c) { return c instanceof C2; }
function isC3(c) { return c instanceof C3; }
function foo2(x) {
if (isC1(x)) {
return x.item;
}
else if (isC2(x)) {
return x.item[0];
}
else if (isC3(x)) {
return x.item;
}
return "error";
}
// More tests
var A = (function () {
function A() {
}
return A;
}());
var A1 = (function (_super) {
__extends(A1, _super);
function A1() {
_super.apply(this, arguments);
}
return A1;
}(A));
var A2 = (function () {
function A2() {
}
return A2;
}());
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
}(A));
function goo(x) {
if (x instanceof A) {
x; // A
}
else {
x; // never
}
if (x instanceof A1) {
x; // A1
}
else {
x; // A
}
if (x instanceof A2) {
x; // A2
}
else {
x; // A
}
if (x instanceof B) {
x; // B
}
else {
x; // A
}
}
Loading

0 comments on commit 3f1ec7a

Please sign in to comment.