diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 71fd9ca42e774..8fd3d846d4534 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14722,6 +14722,18 @@ namespace ts { if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } + if (isPropertyAccessExpression(left) && idText(left.name) === "constructor") { + return narrowTypeByConstructor(type, expr.right, operator, assumeTrue); + } + if (isPropertyAccessExpression(right) && idText(right.name) === "constructor") { + return narrowTypeByConstructor(type, expr.left, operator, assumeTrue); + } + if (isElementAccessExpression(left) && isStringLiteralLike(left.argumentExpression) && left.argumentExpression.text === "constructor") { + return narrowTypeByConstructor(type, expr.right, operator, assumeTrue); + } + if (isElementAccessExpression(right) && isStringLiteralLike(right.argumentExpression) && right.argumentExpression.text === "constructor") { + return narrowTypeByConstructor(type, expr.left, operator, assumeTrue); + } break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); @@ -14741,7 +14753,7 @@ namespace ts { if (type.flags & TypeFlags.Any) { return type; } - if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { + if (isNegatedEqualityToken(operator)) { assumeTrue = !assumeTrue; } const valueType = getTypeOfExpression(value); @@ -14771,6 +14783,26 @@ namespace ts { return type; } + function narrowTypeByConstructor(type: Type, expr: Expression, operator: SyntaxKind, assumeTrue: boolean): Type { + if (!assumeTrue || isNegatedEqualityToken(operator)) return type; + + const rightType = getTypeOfExpression(expr); + if (!isTypeSubtypeOf(rightType, globalFunctionType)) return type; + + const prototypeProperty = getPropertyOfType(rightType, "prototype" as __String); + if (!prototypeProperty) return type; + + const prototypePropertyType = getTypeOfSymbol(prototypeProperty); + const targetType = !isTypeAny(prototypePropertyType) ? prototypePropertyType : undefined; + if (!targetType || isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) return type; + + return getNarrowedType(type, targetType, assumeTrue, isConstructedBy); + + function isConstructedBy(source: Type, target: Type) { + return source.flags & TypeFlags.Primitive ? areTypesComparable(source, target) : isTypeDerivedFrom(source, target); + } + } + function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type { // We have '==', '!=', '====', or !==' operator with 'typeof xxx' and string literal operands const target = getReferenceCandidate(typeOfExpr.expression); @@ -14782,7 +14814,7 @@ namespace ts { } return type; } - if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { + if (isNegatedEqualityToken(operator)) { assumeTrue = !assumeTrue; } if (type.flags & TypeFlags.Any && literal.text === "function") { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b285a9878486e..d36dd00e11fb0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6685,6 +6685,11 @@ namespace ts { return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports; } + /** @internal */ + export function isNegatedEqualityToken(kind: SyntaxKind): boolean { + return kind === SyntaxKind.ExclamationEqualsToken || kind === SyntaxKind.ExclamationEqualsEqualsToken; + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getTokenConstructor(): new (kind: TKind, pos?: number, end?: number) => Token; diff --git a/tests/baselines/reference/typeGuardConstructor.errors.txt b/tests/baselines/reference/typeGuardConstructor.errors.txt new file mode 100644 index 0000000000000..c57f8a54c471b --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructor.errors.txt @@ -0,0 +1,117 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts(45,11): error TS2339: Property 'p2' does not exist on type 'C1 | C2'. + Property 'p2' does not exist on type 'C1'. +tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts(49,11): error TS2339: Property 'p2' does not exist on type 'C1 | C2'. + Property 'p2' does not exist on type 'C1'. +tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts(52,11): error TS2339: Property 'p1' does not exist on type 'C1 | C2'. + Property 'p1' does not exist on type 'C2'. +tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts(63,11): error TS2339: Property 'p4' does not exist on type 'C1 | C2 | C3'. + Property 'p4' does not exist on type 'C1'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts (4 errors) ==== + class C1 { + p1: string; + } + class C2 { + p2: number; + } + class D1 extends C1 { + p3: number; + } + class C3 { + p4: number; + } + class D2 extends D1 { + p5: number + } + + var a: C1; + if (a.constructor === D1) { + a.p3; + } + if (a.constructor == D1) { + a.p3; + } + if (D1 === a.constructor) { + a.p3; + } + if (a["constructor"] === D1) { + a.p3; + } + if (D1 === a["constructor"]) { + a.p3; + } + + var b: C1; + if (b.constructor === D2) { + b.p3; + b.p5; + } + + var ctor3: C1 | C2; + if (ctor3.constructor === C1) { + ctor3.p1; // C1 + } + else { + ctor3.p2; // C2 + ~~ +!!! error TS2339: Property 'p2' does not exist on type 'C1 | C2'. +!!! error TS2339: Property 'p2' does not exist on type 'C1'. + } + + if (ctor3.constructor !== C1) { + ctor3.p2; // C1 + ~~ +!!! error TS2339: Property 'p2' does not exist on type 'C1 | C2'. +!!! error TS2339: Property 'p2' does not exist on type 'C1'. + } + else { + ctor3.p1; // C2 + ~~ +!!! error TS2339: Property 'p1' does not exist on type 'C1 | C2'. +!!! error TS2339: Property 'p1' does not exist on type 'C2'. + } + + var ctor4: C1 | C2 | C3; + if (ctor4.constructor === C1) { + ctor4.p1; // C1 + } + else if (ctor4.constructor === C2) { + ctor4.p2; // C2 + } + else { + ctor4.p4; // C3 + ~~ +!!! error TS2339: Property 'p4' does not exist on type 'C1 | C2 | C3'. +!!! error TS2339: Property 'p4' does not exist on type 'C1'. + } + + var x: number | "hello" | "world" | true | 1[] | undefined; + if (x.constructor === String) { + x.length; + } + + if (x.constructor === Number) { + x.toFixed(); + } + + if (x.constructor === Boolean) { + const b = x; + } + + if(x.constructor === Array) { + const c = x[0]; + } + + + class Bar { + a: string + } + + class Baz { + a: string + } + var bar: Bar | Baz; + if (bar.constructor === Baz) { + const baz = bar + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardConstructor.js b/tests/baselines/reference/typeGuardConstructor.js new file mode 100644 index 0000000000000..ea4f1dd63659c --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructor.js @@ -0,0 +1,207 @@ +//// [typeGuardConstructor.ts] +class C1 { + p1: string; +} +class C2 { + p2: number; +} +class D1 extends C1 { + p3: number; +} +class C3 { + p4: number; +} +class D2 extends D1 { + p5: number +} + +var a: C1; +if (a.constructor === D1) { + a.p3; +} +if (a.constructor == D1) { + a.p3; +} +if (D1 === a.constructor) { + a.p3; +} +if (a["constructor"] === D1) { + a.p3; +} +if (D1 === a["constructor"]) { + a.p3; +} + +var b: C1; +if (b.constructor === D2) { + b.p3; + b.p5; +} + +var ctor3: C1 | C2; +if (ctor3.constructor === C1) { + ctor3.p1; // C1 +} +else { + ctor3.p2; // C2 +} + +if (ctor3.constructor !== C1) { + ctor3.p2; // C1 +} +else { + ctor3.p1; // C2 +} + +var ctor4: C1 | C2 | C3; +if (ctor4.constructor === C1) { + ctor4.p1; // C1 +} +else if (ctor4.constructor === C2) { + ctor4.p2; // C2 +} +else { + ctor4.p4; // C3 +} + +var x: number | "hello" | "world" | true | 1[] | undefined; +if (x.constructor === String) { + x.length; +} + +if (x.constructor === Number) { + x.toFixed(); +} + +if (x.constructor === Boolean) { + const b = x; +} + +if(x.constructor === Array) { + const c = x[0]; +} + + +class Bar { + a: string +} + +class Baz { + a: string +} +var bar: Bar | Baz; +if (bar.constructor === Baz) { + const baz = bar +} + +//// [typeGuardConstructor.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var C1 = /** @class */ (function () { + function C1() { + } + return C1; +}()); +var C2 = /** @class */ (function () { + function C2() { + } + return C2; +}()); +var D1 = /** @class */ (function (_super) { + __extends(D1, _super); + function D1() { + return _super !== null && _super.apply(this, arguments) || this; + } + return D1; +}(C1)); +var C3 = /** @class */ (function () { + function C3() { + } + return C3; +}()); +var D2 = /** @class */ (function (_super) { + __extends(D2, _super); + function D2() { + return _super !== null && _super.apply(this, arguments) || this; + } + return D2; +}(D1)); +var a; +if (a.constructor === D1) { + a.p3; +} +if (a.constructor == D1) { + a.p3; +} +if (D1 === a.constructor) { + a.p3; +} +if (a["constructor"] === D1) { + a.p3; +} +if (D1 === a["constructor"]) { + a.p3; +} +var b; +if (b.constructor === D2) { + b.p3; + b.p5; +} +var ctor3; +if (ctor3.constructor === C1) { + ctor3.p1; // C1 +} +else { + ctor3.p2; // C2 +} +if (ctor3.constructor !== C1) { + ctor3.p2; // C1 +} +else { + ctor3.p1; // C2 +} +var ctor4; +if (ctor4.constructor === C1) { + ctor4.p1; // C1 +} +else if (ctor4.constructor === C2) { + ctor4.p2; // C2 +} +else { + ctor4.p4; // C3 +} +var x; +if (x.constructor === String) { + x.length; +} +if (x.constructor === Number) { + x.toFixed(); +} +if (x.constructor === Boolean) { + var b_1 = x; +} +if (x.constructor === Array) { + var c = x[0]; +} +var Bar = /** @class */ (function () { + function Bar() { + } + return Bar; +}()); +var Baz = /** @class */ (function () { + function Baz() { + } + return Baz; +}()); +var bar; +if (bar.constructor === Baz) { + var baz = bar; +} diff --git a/tests/baselines/reference/typeGuardConstructor.symbols b/tests/baselines/reference/typeGuardConstructor.symbols new file mode 100644 index 0000000000000..05a84e93688ae --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructor.symbols @@ -0,0 +1,259 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts === +class C1 { +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + + p1: string; +>p1 : Symbol(C1.p1, Decl(typeGuardConstructor.ts, 0, 10)) +} +class C2 { +>C2 : Symbol(C2, Decl(typeGuardConstructor.ts, 2, 1)) + + p2: number; +>p2 : Symbol(C2.p2, Decl(typeGuardConstructor.ts, 3, 10)) +} +class D1 extends C1 { +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + + p3: number; +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} +class C3 { +>C3 : Symbol(C3, Decl(typeGuardConstructor.ts, 8, 1)) + + p4: number; +>p4 : Symbol(C3.p4, Decl(typeGuardConstructor.ts, 9, 10)) +} +class D2 extends D1 { +>D2 : Symbol(D2, Decl(typeGuardConstructor.ts, 11, 1)) +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) + + p5: number +>p5 : Symbol(D2.p5, Decl(typeGuardConstructor.ts, 12, 21)) +} + +var a: C1; +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + +if (a.constructor === D1) { +>a.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) + + a.p3; +>a.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} +if (a.constructor == D1) { +>a.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) + + a.p3; +>a.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} +if (D1 === a.constructor) { +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) +>a.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) + + a.p3; +>a.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} +if (a["constructor"] === D1) { +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) + + a.p3; +>a.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} +if (D1 === a["constructor"]) { +>D1 : Symbol(D1, Decl(typeGuardConstructor.ts, 5, 1)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>"constructor" : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) + + a.p3; +>a.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>a : Symbol(a, Decl(typeGuardConstructor.ts, 16, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +} + +var b: C1; +>b : Symbol(b, Decl(typeGuardConstructor.ts, 33, 3)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + +if (b.constructor === D2) { +>b.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>b : Symbol(b, Decl(typeGuardConstructor.ts, 33, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>D2 : Symbol(D2, Decl(typeGuardConstructor.ts, 11, 1)) + + b.p3; +>b.p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) +>b : Symbol(b, Decl(typeGuardConstructor.ts, 33, 3)) +>p3 : Symbol(D1.p3, Decl(typeGuardConstructor.ts, 6, 21)) + + b.p5; +>b.p5 : Symbol(D2.p5, Decl(typeGuardConstructor.ts, 12, 21)) +>b : Symbol(b, Decl(typeGuardConstructor.ts, 33, 3)) +>p5 : Symbol(D2.p5, Decl(typeGuardConstructor.ts, 12, 21)) +} + +var ctor3: C1 | C2; +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) +>C2 : Symbol(C2, Decl(typeGuardConstructor.ts, 2, 1)) + +if (ctor3.constructor === C1) { +>ctor3.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + + ctor3.p1; // C1 +>ctor3.p1 : Symbol(C1.p1, Decl(typeGuardConstructor.ts, 0, 10)) +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +>p1 : Symbol(C1.p1, Decl(typeGuardConstructor.ts, 0, 10)) +} +else { + ctor3.p2; // C2 +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +} + +if (ctor3.constructor !== C1) { +>ctor3.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + + ctor3.p2; // C1 +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +} +else { + ctor3.p1; // C2 +>ctor3 : Symbol(ctor3, Decl(typeGuardConstructor.ts, 39, 3)) +} + +var ctor4: C1 | C2 | C3; +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) +>C2 : Symbol(C2, Decl(typeGuardConstructor.ts, 2, 1)) +>C3 : Symbol(C3, Decl(typeGuardConstructor.ts, 8, 1)) + +if (ctor4.constructor === C1) { +>ctor4.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>C1 : Symbol(C1, Decl(typeGuardConstructor.ts, 0, 0)) + + ctor4.p1; // C1 +>ctor4.p1 : Symbol(C1.p1, Decl(typeGuardConstructor.ts, 0, 10)) +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +>p1 : Symbol(C1.p1, Decl(typeGuardConstructor.ts, 0, 10)) +} +else if (ctor4.constructor === C2) { +>ctor4.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>C2 : Symbol(C2, Decl(typeGuardConstructor.ts, 2, 1)) + + ctor4.p2; // C2 +>ctor4.p2 : Symbol(C2.p2, Decl(typeGuardConstructor.ts, 3, 10)) +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +>p2 : Symbol(C2.p2, Decl(typeGuardConstructor.ts, 3, 10)) +} +else { + ctor4.p4; // C3 +>ctor4 : Symbol(ctor4, Decl(typeGuardConstructor.ts, 54, 3)) +} + +var x: number | "hello" | "world" | true | 1[] | undefined; +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) + +if (x.constructor === String) { +>x.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>String : Symbol(String, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x.length; +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +if (x.constructor === Number) { +>x.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>Number : Symbol(Number, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x.toFixed(); +>x.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --)) +} + +if (x.constructor === Boolean) { +>x.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + const b = x; +>b : Symbol(b, Decl(typeGuardConstructor.ts, 75, 9)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +} + +if(x.constructor === Array) { +>x.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + const c = x[0]; +>c : Symbol(c, Decl(typeGuardConstructor.ts, 79, 9)) +>x : Symbol(x, Decl(typeGuardConstructor.ts, 65, 3)) +} + + +class Bar { +>Bar : Symbol(Bar, Decl(typeGuardConstructor.ts, 80, 1)) + + a: string +>a : Symbol(Bar.a, Decl(typeGuardConstructor.ts, 83, 11)) +} + +class Baz { +>Baz : Symbol(Baz, Decl(typeGuardConstructor.ts, 85, 1)) + + a: string +>a : Symbol(Baz.a, Decl(typeGuardConstructor.ts, 87, 11)) +} +var bar: Bar | Baz; +>bar : Symbol(bar, Decl(typeGuardConstructor.ts, 90, 3)) +>Bar : Symbol(Bar, Decl(typeGuardConstructor.ts, 80, 1)) +>Baz : Symbol(Baz, Decl(typeGuardConstructor.ts, 85, 1)) + +if (bar.constructor === Baz) { +>bar.constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>bar : Symbol(bar, Decl(typeGuardConstructor.ts, 90, 3)) +>constructor : Symbol(Object.constructor, Decl(lib.d.ts, --, --)) +>Baz : Symbol(Baz, Decl(typeGuardConstructor.ts, 85, 1)) + + const baz = bar +>baz : Symbol(baz, Decl(typeGuardConstructor.ts, 92, 9)) +>bar : Symbol(bar, Decl(typeGuardConstructor.ts, 90, 3)) +} diff --git a/tests/baselines/reference/typeGuardConstructor.types b/tests/baselines/reference/typeGuardConstructor.types new file mode 100644 index 0000000000000..2911d6d306ea8 --- /dev/null +++ b/tests/baselines/reference/typeGuardConstructor.types @@ -0,0 +1,288 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts === +class C1 { +>C1 : C1 + + p1: string; +>p1 : string +} +class C2 { +>C2 : C2 + + p2: number; +>p2 : number +} +class D1 extends C1 { +>D1 : D1 +>C1 : C1 + + p3: number; +>p3 : number +} +class C3 { +>C3 : C3 + + p4: number; +>p4 : number +} +class D2 extends D1 { +>D2 : D2 +>D1 : D1 + + p5: number +>p5 : number +} + +var a: C1; +>a : C1 +>C1 : C1 + +if (a.constructor === D1) { +>a.constructor === D1 : boolean +>a.constructor : Function +>a : C1 +>constructor : Function +>D1 : typeof D1 + + a.p3; +>a.p3 : number & D1 +>a : D1 +>p3 : number & D1 +} +if (a.constructor == D1) { +>a.constructor == D1 : boolean +>a.constructor : Function +>a : C1 +>constructor : Function +>D1 : typeof D1 + + a.p3; +>a.p3 : number & D1 +>a : D1 +>p3 : number & D1 +} +if (D1 === a.constructor) { +>D1 === a.constructor : boolean +>D1 : typeof D1 +>a.constructor : Function +>a : C1 +>constructor : Function + + a.p3; +>a.p3 : number & D1 +>a : D1 +>p3 : number & D1 +} +if (a["constructor"] === D1) { +>a["constructor"] === D1 : boolean +>a["constructor"] : Function +>a : C1 +>"constructor" : "constructor" +>D1 : typeof D1 + + a.p3; +>a.p3 : number & D1 +>a : D1 +>p3 : number & D1 +} +if (D1 === a["constructor"]) { +>D1 === a["constructor"] : boolean +>D1 : typeof D1 +>a["constructor"] : Function +>a : C1 +>"constructor" : "constructor" + + a.p3; +>a.p3 : number & D1 +>a : D1 +>p3 : number & D1 +} + +var b: C1; +>b : C1 +>C1 : C1 + +if (b.constructor === D2) { +>b.constructor === D2 : boolean +>b.constructor : Function +>b : C1 +>constructor : Function +>D2 : typeof D2 + + b.p3; +>b.p3 : number & D2 +>b : D2 +>p3 : number & D2 + + b.p5; +>b.p5 : number & D2 +>b : D2 +>p5 : number & D2 +} + +var ctor3: C1 | C2; +>ctor3 : C1 | C2 +>C1 : C1 +>C2 : C2 + +if (ctor3.constructor === C1) { +>ctor3.constructor === C1 : boolean +>ctor3.constructor : Function +>ctor3 : C1 | C2 +>constructor : Function +>C1 : typeof C1 + + ctor3.p1; // C1 +>ctor3.p1 : string & C1 +>ctor3 : C1 +>p1 : string & C1 +} +else { + ctor3.p2; // C2 +>ctor3.p2 : any +>ctor3 : C1 | C2 +>p2 : any +} + +if (ctor3.constructor !== C1) { +>ctor3.constructor !== C1 : boolean +>ctor3.constructor : Function +>ctor3 : C1 | C2 +>constructor : Function +>C1 : typeof C1 + + ctor3.p2; // C1 +>ctor3.p2 : any +>ctor3 : C1 | C2 +>p2 : any +} +else { + ctor3.p1; // C2 +>ctor3.p1 : any +>ctor3 : C1 | C2 +>p1 : any +} + +var ctor4: C1 | C2 | C3; +>ctor4 : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 + +if (ctor4.constructor === C1) { +>ctor4.constructor === C1 : boolean +>ctor4.constructor : Function +>ctor4 : C1 | C2 | C3 +>constructor : Function +>C1 : typeof C1 + + ctor4.p1; // C1 +>ctor4.p1 : string & C1 +>ctor4 : C1 +>p1 : string & C1 +} +else if (ctor4.constructor === C2) { +>ctor4.constructor === C2 : boolean +>ctor4.constructor : Function +>ctor4 : C1 | C2 | C3 +>constructor : Function +>C2 : typeof C2 + + ctor4.p2; // C2 +>ctor4.p2 : number & C2 +>ctor4 : C2 +>p2 : number & C2 +} +else { + ctor4.p4; // C3 +>ctor4.p4 : any +>ctor4 : C1 | C2 | C3 +>p4 : any +} + +var x: number | "hello" | "world" | true | 1[] | undefined; +>x : number | true | "hello" | "world" | 1[] +>true : true + +if (x.constructor === String) { +>x.constructor === String : boolean +>x.constructor : Function +>x : number | true | "hello" | "world" | 1[] +>constructor : Function +>String : StringConstructor + + x.length; +>x.length : number & String +>x : "hello" | "world" +>length : number & String +} + +if (x.constructor === Number) { +>x.constructor === Number : boolean +>x.constructor : Function +>x : number | true | "hello" | "world" | 1[] +>constructor : Function +>Number : NumberConstructor + + x.toFixed(); +>x.toFixed() : string +>x.toFixed : (fractionDigits?: number) => string +>x : number +>toFixed : (fractionDigits?: number) => string +} + +if (x.constructor === Boolean) { +>x.constructor === Boolean : boolean +>x.constructor : Function +>x : number | true | "hello" | "world" | 1[] +>constructor : Function +>Boolean : BooleanConstructor + + const b = x; +>b : true +>x : true +} + +if(x.constructor === Array) { +>x.constructor === Array : boolean +>x.constructor : Function +>x : number | true | "hello" | "world" | 1[] +>constructor : Function +>Array : ArrayConstructor + + const c = x[0]; +>c : 1 +>x[0] : 1 +>x : 1[] +>0 : 0 +} + + +class Bar { +>Bar : Bar + + a: string +>a : string +} + +class Baz { +>Baz : Baz + + a: string +>a : string +} +var bar: Bar | Baz; +>bar : Bar | Baz +>Bar : Bar +>Baz : Baz + +if (bar.constructor === Baz) { +>bar.constructor === Baz : boolean +>bar.constructor : Function +>bar : Bar | Baz +>constructor : Function +>Baz : typeof Baz + + const baz = bar +>baz : Baz +>bar : Baz +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts new file mode 100644 index 0000000000000..b4c8634a4e852 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardConstructor.ts @@ -0,0 +1,94 @@ +class C1 { + p1: string; +} +class C2 { + p2: number; +} +class D1 extends C1 { + p3: number; +} +class C3 { + p4: number; +} +class D2 extends D1 { + p5: number +} + +var a: C1; +if (a.constructor === D1) { + a.p3; +} +if (a.constructor == D1) { + a.p3; +} +if (D1 === a.constructor) { + a.p3; +} +if (a["constructor"] === D1) { + a.p3; +} +if (D1 === a["constructor"]) { + a.p3; +} + +var b: C1; +if (b.constructor === D2) { + b.p3; + b.p5; +} + +var ctor3: C1 | C2; +if (ctor3.constructor === C1) { + ctor3.p1; // C1 +} +else { + ctor3.p2; // C2 +} + +if (ctor3.constructor !== C1) { + ctor3.p2; // C1 +} +else { + ctor3.p1; // C2 +} + +var ctor4: C1 | C2 | C3; +if (ctor4.constructor === C1) { + ctor4.p1; // C1 +} +else if (ctor4.constructor === C2) { + ctor4.p2; // C2 +} +else { + ctor4.p4; // C3 +} + +var x: number | "hello" | "world" | true | 1[] | undefined; +if (x.constructor === String) { + x.length; +} + +if (x.constructor === Number) { + x.toFixed(); +} + +if (x.constructor === Boolean) { + const b = x; +} + +if(x.constructor === Array) { + const c = x[0]; +} + + +class Bar { + a: string +} + +class Baz { + a: string +} +var bar: Bar | Baz; +if (bar.constructor === Baz) { + const baz = bar +} \ No newline at end of file