From 9c3c2adfb0b9fd515af2c78109bcfdb139475549 Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Wed, 19 Apr 2017 00:08:54 +0300 Subject: [PATCH 1/4] Add initial support for 'in' typeguarding --- src/compiler/binder.ts | 6 + src/compiler/checker.ts | 14 + .../reference/inKeywordTypeguard.errors.txt | 154 ++++++++++ .../baselines/reference/inKeywordTypeguard.js | 230 +++++++++++++++ .../typeGuardOfFromPropNameInUnionType.js | 184 ++++++++++++ ...typeGuardOfFromPropNameInUnionType.symbols | 252 ++++++++++++++++ .../typeGuardOfFromPropNameInUnionType.types | 273 ++++++++++++++++++ tests/cases/compiler/inKeywordTypeguard.ts | 97 +++++++ .../typeGuardOfFromPropNameInUnionType.ts | 77 +++++ 9 files changed, 1287 insertions(+) create mode 100644 tests/baselines/reference/inKeywordTypeguard.errors.txt create mode 100644 tests/baselines/reference/inKeywordTypeguard.js create mode 100644 tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js create mode 100644 tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols create mode 100644 tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types create mode 100644 tests/cases/compiler/inKeywordTypeguard.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 71b5e1afbffea..4eab97f28bb27 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -749,6 +749,10 @@ namespace ts { return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((expr1).expression) && expr2.kind === SyntaxKind.StringLiteral; } + function isNarrowableInOperands(left: Expression, right: Expression) { + return left.kind === SyntaxKind.StringLiteral && isNarrowingExpression(right); + } + function isNarrowingBinaryExpression(expr: BinaryExpression) { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: @@ -761,6 +765,8 @@ namespace ts { isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right); case SyntaxKind.InstanceOfKeyword: return isNarrowableOperand(expr.left); + case SyntaxKind.InKeyword: + return isNarrowableInOperands(expr.left, expr.right); case SyntaxKind.CommaToken: return isNarrowingExpression(expr.right); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 25d967795a7ea..7f7a75a33f959 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12722,6 +12722,14 @@ namespace ts { return type; } + function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { + if ((type.flags & (TypeFlags.Union | TypeFlags.Object)) || (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType)) { + const propName = literal.text; + return filterType(type, t => !!getPropertyOfType(t, propName) === assumeTrue); + } + return type; + } + function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: @@ -12757,6 +12765,12 @@ namespace ts { break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); + case SyntaxKind.InKeyword: + const target = getReferenceCandidate(expr.right); + if (expr.left.kind === SyntaxKind.StringLiteral && isMatchingReference(reference, target)) { + return narrowByInKeyword(type, expr.left, assumeTrue); + } + break; case SyntaxKind.CommaToken: return narrowType(type, expr.right, assumeTrue); } diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt new file mode 100644 index 0000000000000..4ee09e71b609e --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard.errors.txt @@ -0,0 +1,154 @@ +tests/cases/compiler/inKeywordTypeguard.ts(6,11): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(8,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(14,11): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'. + Property 'a' does not exist on type 'BWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'. + Property 'b' does not exist on type 'AWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(62,11): error TS2339: Property 'b' does not exist on type 'A | C | D'. + Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(64,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(72,32): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'. + + +==== tests/cases/compiler/inKeywordTypeguard.ts (16 errors) ==== + class A { a: string; } + class B { b: string; } + + function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class AOpt { a?: string } + class BOpn { b?: string } + + function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } + } + + class AWithMethod { + a(): string { return "" } + } + + class BWithMethod { + b(): string { return "" } + } + + function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'. + } else { + } + } + + function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + ~ +!!! error TS2339: Property 'a' does not exist on type 'never'. + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type 'never'. + } else { + x.a(); + ~ +!!! error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'. +!!! error TS2339: Property 'a' does not exist on type 'BWithMethod'. + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'. +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'. + } + } + + class C { a: string } + class D { a: string } + + function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A | C | D'. +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class ClassWithProp { prop: A | B } + + function negativePropTest(x: ClassWithProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + let z: string = x.prop.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class NegativeClassTest { + protected prop: A | B; + inThis() { + if ('a' in this.prop) { + let z: number = this.prop.b; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + let y: string = this.prop.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + } + + class UnreachableCodeDetection { + a: string; + inThis() { + if ('a' in this) { + } else { + let y = this.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'never'. + } + } + } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js new file mode 100644 index 0000000000000..33e8eefa14650 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard.js @@ -0,0 +1,230 @@ +//// [inKeywordTypeguard.ts] +class A { a: string; } +class B { b: string; } + +function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class AOpt { a?: string } +class BOpn { b?: string } + +function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } +} + +class AWithMethod { + a(): string { return "" } +} + +class BWithMethod { + b(): string { return "" } +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + x.b(); + } else { + x.a(); + x.b(); + } +} + +class C { a: string } +class D { a: string } + +function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class ClassWithProp { prop: A | B } + +function negativePropTest(x: ClassWithProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + } else { + let z: string = x.prop.a; + } +} + +class NegativeClassTest { + protected prop: A | B; + inThis() { + if ('a' in this.prop) { + let z: number = this.prop.b; + } else { + let y: string = this.prop.a; + } + } +} + +class UnreachableCodeDetection { + a: string; + inThis() { + if ('a' in this) { + } else { + let y = this.a; + } + } +} + +//// [inKeywordTypeguard.js] +var A = (function () { + function A() { + } + return A; +}()); +var B = (function () { + function B() { + } + return B; +}()); +function negativeClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +function positiveClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +var AOpt = (function () { + function AOpt() { + } + return AOpt; +}()); +var BOpn = (function () { + function BOpn() { + } + return BOpn; +}()); +function positiveTestClassesWithOptionalProperties(x) { + if ("a" in x) { + x.a = "1"; + } + else { + x.b = "1"; + } +} +var AWithMethod = (function () { + function AWithMethod() { + } + AWithMethod.prototype.a = function () { return ""; }; + return AWithMethod; +}()); +var BWithMethod = (function () { + function BWithMethod() { + } + BWithMethod.prototype.b = function () { return ""; }; + return BWithMethod; +}()); +function negativeTestClassesWithMembers(x) { + if ("a" in x) { + x.a(); + x.b(); + } + else { + } +} +function negativeTestClassesWithMemberMissingInBothClasses(x) { + if ("c" in x) { + x.a(); + x.b(); + } + else { + x.a(); + x.b(); + } +} +var C = (function () { + function C() { + } + return C; +}()); +var D = (function () { + function D() { + } + return D; +}()); +function negativeMultipleClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +var ClassWithProp = (function () { + function ClassWithProp() { + } + return ClassWithProp; +}()); +function negativePropTest(x) { + if ("a" in x.prop) { + var y = x.prop.b; + } + else { + var z = x.prop.a; + } +} +var NegativeClassTest = (function () { + function NegativeClassTest() { + } + NegativeClassTest.prototype.inThis = function () { + if ('a' in this.prop) { + var z = this.prop.b; + } + else { + var y = this.prop.a; + } + }; + return NegativeClassTest; +}()); +var UnreachableCodeDetection = (function () { + function UnreachableCodeDetection() { + } + UnreachableCodeDetection.prototype.inThis = function () { + if ('a' in this) { + } + else { + var y = this.a; + } + }; + return UnreachableCodeDetection; +}()); diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js new file mode 100644 index 0000000000000..644abfaf33ff8 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js @@ -0,0 +1,184 @@ +//// [typeGuardOfFromPropNameInUnionType.ts] +class A { a: string; } +class B { b: number; } +class C { b: Object; } +class D { a: Date; } +class ClassWithProp { prop: A | B } +class NestedClassWithProp { outer: ClassWithProp } + +function namedClasses(x: A | B) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = 1; + } +} + +function multipleClasses(x: A | B | C | D) { + if ("a" in x) { + let y: string | Date = x.a; + } else { + let z: number | Object = x.b; + } +} + +function anonymousClasses(x: { a: string; } | { b: number; }) { + if ("a" in x) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} +function inParenthesizedExpression(x: A | B) { + if ("a" in (x)) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} + + +function inProperty(x: ClassWithProp) { + if ("a" in x.prop) { + let y: string = x.prop.a; + } else { + let z: number = x.prop.b; + } +} + + +function innestedProperty(x: NestedClassWithProp) { + if ("a" in x.outer.prop) { + let y: string = x.outer.prop.a; + } else { + let z: number = x.outer.prop.b; + } +} + +class InMemberOfClass { + protected prop: A | B; + inThis() { + if ('a' in this.prop) { + let y: string = this.prop.a; + } else { + let z: number = this.prop.b; + } + } +} + +//added for completeness +class SelfAssert { + a: string; + inThis() { + if ('a' in this) { + let y: string = this.a; + } else { + } + } +} + +//// [typeGuardOfFromPropNameInUnionType.js] +var A = (function () { + function A() { + } + return A; +}()); +var B = (function () { + function B() { + } + return B; +}()); +var C = (function () { + function C() { + } + return C; +}()); +var D = (function () { + function D() { + } + return D; +}()); +var ClassWithProp = (function () { + function ClassWithProp() { + } + return ClassWithProp; +}()); +var NestedClassWithProp = (function () { + function NestedClassWithProp() { + } + return NestedClassWithProp; +}()); +function namedClasses(x) { + if ("a" in x) { + x.a = "1"; + } + else { + x.b = 1; + } +} +function multipleClasses(x) { + if ("a" in x) { + var y = x.a; + } + else { + var z = x.b; + } +} +function anonymousClasses(x) { + if ("a" in x) { + var y = x.a; + } + else { + var z = x.b; + } +} +function inParenthesizedExpression(x) { + if ("a" in (x)) { + var y = x.a; + } + else { + var z = x.b; + } +} +function inProperty(x) { + if ("a" in x.prop) { + var y = x.prop.a; + } + else { + var z = x.prop.b; + } +} +function innestedProperty(x) { + if ("a" in x.outer.prop) { + var y = x.outer.prop.a; + } + else { + var z = x.outer.prop.b; + } +} +var InMemberOfClass = (function () { + function InMemberOfClass() { + } + InMemberOfClass.prototype.inThis = function () { + if ('a' in this.prop) { + var y = this.prop.a; + } + else { + var z = this.prop.b; + } + }; + return InMemberOfClass; +}()); +//added for completeness +var SelfAssert = (function () { + function SelfAssert() { + } + SelfAssert.prototype.inThis = function () { + if ('a' in this) { + var y = this.a; + } + else { + } + }; + return SelfAssert; +}()); diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols new file mode 100644 index 0000000000000..0302635b558af --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols @@ -0,0 +1,252 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts === +class A { a: string; } +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + +class B { b: number; } +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + +class C { b: Object; } +>C : Symbol(C, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 22)) +>b : Symbol(C.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 9)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +class D { a: Date; } +>D : Symbol(D, Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 22)) +>a : Symbol(D.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +class ClassWithProp { prop: A | B } +>ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) + +class NestedClassWithProp { outer: ClassWithProp } +>NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 35)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) + +function namedClasses(x: A | B) { +>namedClasses : Symbol(namedClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 50)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) + + x.a = "1"; +>x.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + + } else { + x.b = 1; +>x.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + } +} + +function multipleClasses(x: A | B | C | D) { +>multipleClasses : Symbol(multipleClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) +>C : Symbol(C, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 22)) +>D : Symbol(D, Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) + + let y: string | Date = x.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 17, 11)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) + + } else { + let z: number | Object = x.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 19, 11)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 9)) + } +} + +function anonymousClasses(x: { a: string; } | { b: number; }) { +>anonymousClasses : Symbol(anonymousClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) +>a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) +>b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) + + if ("a" in x) { +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) + + let y: string = x.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 25, 11)) +>x.a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) +>a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) + + } else { + let z: number = x.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 27, 11)) +>x.b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) +>b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) + } +} +function inParenthesizedExpression(x: A | B) { +>inParenthesizedExpression : Symbol(inParenthesizedExpression, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) + + if ("a" in (x)) { +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) + + let y: string = x.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 11)) +>x.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + + } else { + let z: number = x.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 34, 11)) +>x.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + } +} + + +function inProperty(x: ClassWithProp) { +>inProperty : Symbol(inProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 36, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) +>ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) + + if ("a" in x.prop) { +>x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) + + let y: string = x.prop.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 41, 11)) +>x.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) +>x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + + } else { + let z: number = x.prop.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 43, 11)) +>x.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) +>x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + } +} + + +function innestedProperty(x: NestedClassWithProp) { +>innestedProperty : Symbol(innestedProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 45, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) +>NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 35)) + + if ("a" in x.outer.prop) { +>x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) + + let y: string = x.outer.prop.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 11)) +>x.outer.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) +>x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + + } else { + let z: number = x.outer.prop.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 11)) +>x.outer.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) +>x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) +>prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + } +} + +class InMemberOfClass { +>InMemberOfClass : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) + + protected prop: A | B; +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) + + inThis() { +>inThis : Symbol(InMemberOfClass.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 57, 26)) + + if ('a' in this.prop) { +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) + + let y: string = this.prop.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 15)) +>this.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) + + } else { + let z: number = this.prop.b; +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 15)) +>this.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) + } + } +} + +//added for completeness +class SelfAssert { +>SelfAssert : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) + + a: string; +>a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) + + inThis() { +>inThis : Symbol(SelfAssert.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 69, 14)) + + if ('a' in this) { +>this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) + + let y: string = this.a; +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 72, 15)) +>this.a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) +>this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) +>a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) + + } else { + } + } +} diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types new file mode 100644 index 0000000000000..d9984840fb3b1 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types @@ -0,0 +1,273 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts === +class A { a: string; } +>A : A +>a : string + +class B { b: number; } +>B : B +>b : number + +class C { b: Object; } +>C : C +>b : Object +>Object : Object + +class D { a: Date; } +>D : D +>a : Date +>Date : Date + +class ClassWithProp { prop: A | B } +>ClassWithProp : ClassWithProp +>prop : A | B +>A : A +>B : B + +class NestedClassWithProp { outer: ClassWithProp } +>NestedClassWithProp : NestedClassWithProp +>outer : ClassWithProp +>ClassWithProp : ClassWithProp + +function namedClasses(x: A | B) { +>namedClasses : (x: A | B) => void +>x : A | B +>A : A +>B : B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.a = "1"; +>x.a = "1" : "1" +>x.a : string +>x : A +>a : string +>"1" : "1" + + } else { + x.b = 1; +>x.b = 1 : 1 +>x.b : number +>x : B +>b : number +>1 : 1 + } +} + +function multipleClasses(x: A | B | C | D) { +>multipleClasses : (x: A | B | C | D) => void +>x : A | B | C | D +>A : A +>B : B +>C : C +>D : D + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B | C | D + + let y: string | Date = x.a; +>y : string | Date +>Date : Date +>x.a : string | Date +>x : A | D +>a : string | Date + + } else { + let z: number | Object = x.b; +>z : number | Object +>Object : Object +>x.b : number | Object +>x : B | C +>b : number | Object + } +} + +function anonymousClasses(x: { a: string; } | { b: number; }) { +>anonymousClasses : (x: { a: string; } | { b: number; }) => void +>x : { a: string; } | { b: number; } +>a : string +>b : number + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } | { b: number; } + + let y: string = x.a; +>y : string +>x.a : string +>x : { a: string; } +>a : string + + } else { + let z: number = x.b; +>z : number +>x.b : number +>x : { b: number; } +>b : number + } +} +function inParenthesizedExpression(x: A | B) { +>inParenthesizedExpression : (x: A | B) => void +>x : A | B +>A : A +>B : B + + if ("a" in (x)) { +>"a" in (x) : boolean +>"a" : "a" +>(x) : A | B +>x : A | B + + let y: string = x.a; +>y : string +>x.a : string +>x : A +>a : string + + } else { + let z: number = x.b; +>z : number +>x.b : number +>x : B +>b : number + } +} + + +function inProperty(x: ClassWithProp) { +>inProperty : (x: ClassWithProp) => void +>x : ClassWithProp +>ClassWithProp : ClassWithProp + + if ("a" in x.prop) { +>"a" in x.prop : boolean +>"a" : "a" +>x.prop : A | B +>x : ClassWithProp +>prop : A | B + + let y: string = x.prop.a; +>y : string +>x.prop.a : string +>x.prop : A +>x : ClassWithProp +>prop : A +>a : string + + } else { + let z: number = x.prop.b; +>z : number +>x.prop.b : number +>x.prop : B +>x : ClassWithProp +>prop : B +>b : number + } +} + + +function innestedProperty(x: NestedClassWithProp) { +>innestedProperty : (x: NestedClassWithProp) => void +>x : NestedClassWithProp +>NestedClassWithProp : NestedClassWithProp + + if ("a" in x.outer.prop) { +>"a" in x.outer.prop : boolean +>"a" : "a" +>x.outer.prop : A | B +>x.outer : ClassWithProp +>x : NestedClassWithProp +>outer : ClassWithProp +>prop : A | B + + let y: string = x.outer.prop.a; +>y : string +>x.outer.prop.a : string +>x.outer.prop : A +>x.outer : ClassWithProp +>x : NestedClassWithProp +>outer : ClassWithProp +>prop : A +>a : string + + } else { + let z: number = x.outer.prop.b; +>z : number +>x.outer.prop.b : number +>x.outer.prop : B +>x.outer : ClassWithProp +>x : NestedClassWithProp +>outer : ClassWithProp +>prop : B +>b : number + } +} + +class InMemberOfClass { +>InMemberOfClass : InMemberOfClass + + protected prop: A | B; +>prop : A | B +>A : A +>B : B + + inThis() { +>inThis : () => void + + if ('a' in this.prop) { +>'a' in this.prop : boolean +>'a' : "a" +>this.prop : A | B +>this : this +>prop : A | B + + let y: string = this.prop.a; +>y : string +>this.prop.a : string +>this.prop : A +>this : this +>prop : A +>a : string + + } else { + let z: number = this.prop.b; +>z : number +>this.prop.b : number +>this.prop : B +>this : this +>prop : B +>b : number + } + } +} + +//added for completeness +class SelfAssert { +>SelfAssert : SelfAssert + + a: string; +>a : string + + inThis() { +>inThis : () => void + + if ('a' in this) { +>'a' in this : boolean +>'a' : "a" +>this : this + + let y: string = this.a; +>y : string +>this.a : string +>this : this +>a : string + + } else { + } + } +} diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts new file mode 100644 index 0000000000000..90d1942e7c5a6 --- /dev/null +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -0,0 +1,97 @@ +class A { a: string; } +class B { b: string; } + +function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class AOpt { a?: string } +class BOpn { b?: string } + +function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } +} + +class AWithMethod { + a(): string { return "" } +} + +class BWithMethod { + b(): string { return "" } +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + x.b(); + } else { + x.a(); + x.b(); + } +} + +class C { a: string } +class D { a: string } + +function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class ClassWithProp { prop: A | B } + +function negativePropTest(x: ClassWithProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + } else { + let z: string = x.prop.a; + } +} + +class NegativeClassTest { + protected prop: A | B; + inThis() { + if ('a' in this.prop) { + let z: number = this.prop.b; + } else { + let y: string = this.prop.a; + } + } +} + +class UnreachableCodeDetection { + a: string; + inThis() { + if ('a' in this) { + } else { + let y = this.a; + } + } +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts new file mode 100644 index 0000000000000..2a14fd2aea87a --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts @@ -0,0 +1,77 @@ +class A { a: string; } +class B { b: number; } +class C { b: Object; } +class D { a: Date; } +class ClassWithProp { prop: A | B } +class NestedClassWithProp { outer: ClassWithProp } + +function namedClasses(x: A | B) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = 1; + } +} + +function multipleClasses(x: A | B | C | D) { + if ("a" in x) { + let y: string | Date = x.a; + } else { + let z: number | Object = x.b; + } +} + +function anonymousClasses(x: { a: string; } | { b: number; }) { + if ("a" in x) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} +function inParenthesizedExpression(x: A | B) { + if ("a" in (x)) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} + + +function inProperty(x: ClassWithProp) { + if ("a" in x.prop) { + let y: string = x.prop.a; + } else { + let z: number = x.prop.b; + } +} + + +function innestedProperty(x: NestedClassWithProp) { + if ("a" in x.outer.prop) { + let y: string = x.outer.prop.a; + } else { + let z: number = x.outer.prop.b; + } +} + +class InMemberOfClass { + protected prop: A | B; + inThis() { + if ('a' in this.prop) { + let y: string = this.prop.a; + } else { + let z: number = this.prop.b; + } + } +} + +//added for completeness +class SelfAssert { + a: string; + inThis() { + if ('a' in this) { + let y: string = this.a; + } else { + } + } +} \ No newline at end of file From a5d6be150bdbe27c501fb2880a73c8c8f4cdc63e Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Wed, 19 Apr 2017 01:38:55 +0300 Subject: [PATCH 2/4] Remove whitespace --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f7a75a33f959..1fc35e174dca2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12725,7 +12725,7 @@ namespace ts { function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { if ((type.flags & (TypeFlags.Union | TypeFlags.Object)) || (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType)) { const propName = literal.text; - return filterType(type, t => !!getPropertyOfType(t, propName) === assumeTrue); + return filterType(type, t => !!getPropertyOfType(t, propName) === assumeTrue); } return type; } From 069f73d0f20e2b8957fa7671c06a0a16160c71a8 Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Fri, 21 Apr 2017 02:03:25 +0300 Subject: [PATCH 3/4] Change type narrowing for optional properties --- src/compiler/checker.ts | 22 +- .../reference/inKeywordTypeguard.errors.txt | 29 ++- .../baselines/reference/inKeywordTypeguard.js | 44 ++-- .../typeGuardOfFromPropNameInUnionType.js | 72 +++-- ...typeGuardOfFromPropNameInUnionType.symbols | 245 ++++++++++-------- .../typeGuardOfFromPropNameInUnionType.types | 107 +++++--- tests/cases/compiler/inKeywordTypeguard.ts | 192 +++++++------- .../typeGuardOfFromPropNameInUnionType.ts | 166 ++++++------ 8 files changed, 517 insertions(+), 360 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1fc35e174dca2..7f2db45bab262 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12722,10 +12722,30 @@ namespace ts { return type; } + function isTypePresencePossible(type: Type, propName: string, shouldHaveProperty: boolean) { + const prop = getPropertyOfType(type, propName); + if (!prop) { + // if there is NO property: + // - but we assume type SHOULD have it then presence of object of following type IS NOT possible + // - and we assume type SHOULD NOT have it then presence of object of following type IS possible + return !shouldHaveProperty; + } else if (prop.flags & SymbolFlags.Optional) { + // if there is an optional property: + // - and we assume type SHOULD have it then presence of object of following type IS possible + // - but assume type SHOULD NOT have it then presence of object of following type IS still possible + return true; + } else /* if (prop.flags & SymbolFlags.Required) */ { + // if there is a required property: + // - and we assume type SHOULD have it then presence of object of following type IS possible + // - but we assume type SHOULD NOT have it then presence of object of following type IS NOT possible + return shouldHaveProperty; + } + } + function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { if ((type.flags & (TypeFlags.Union | TypeFlags.Object)) || (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType)) { const propName = literal.text; - return filterType(type, t => !!getPropertyOfType(t, propName) === assumeTrue); + return filterType(type, t => isTypePresencePossible(t, propName, /* shouldHaveProperty */ assumeTrue)); } return type; } diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt index 4ee09e71b609e..914daf42a7e98 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard.errors.txt @@ -2,6 +2,8 @@ tests/cases/compiler/inKeywordTypeguard.ts(6,11): error TS2339: Property 'b' doe tests/cases/compiler/inKeywordTypeguard.ts(8,11): error TS2339: Property 'a' does not exist on type 'B'. tests/cases/compiler/inKeywordTypeguard.ts(14,11): error TS2339: Property 'b' does not exist on type 'A'. tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'. + Property 'b' does not exist on type 'AWithOptionalProp'. tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'. tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type 'never'. tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type 'never'. @@ -19,7 +21,7 @@ tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' do tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'. -==== tests/cases/compiler/inKeywordTypeguard.ts (16 errors) ==== +==== tests/cases/compiler/inKeywordTypeguard.ts (17 errors) ==== class A { a: string; } class B { b: string; } @@ -47,23 +49,26 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do } } - class AOpt { a?: string } - class BOpn { b?: string } + class AWithOptionalProp { a?: string; } + class BWithOptionalProp { b?: string; } - function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { + function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { if ("a" in x) { x.a = "1"; } else { x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'. +!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp'. } } class AWithMethod { - a(): string { return "" } + a(): string { return ""; } } class BWithMethod { - b(): string { return "" } + b(): string { return ""; } } function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { @@ -96,8 +101,8 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do } } - class C { a: string } - class D { a: string } + class C { a: string; } + class D { a: string; } function negativeMultipleClassesTest(x: A | B | C | D) { if ("a" in x) { @@ -112,9 +117,9 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do } } - class ClassWithProp { prop: A | B } + class ClassWithUnionProp { prop: A | B } - function negativePropTest(x: ClassWithProp) { + function negativePropTest(x: ClassWithUnionProp) { if ("a" in x.prop) { let y: string = x.prop.b; ~ @@ -129,7 +134,7 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do class NegativeClassTest { protected prop: A | B; inThis() { - if ('a' in this.prop) { + if ("a" in this.prop) { let z: number = this.prop.b; ~ !!! error TS2339: Property 'b' does not exist on type 'A'. @@ -144,7 +149,7 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do class UnreachableCodeDetection { a: string; inThis() { - if ('a' in this) { + if ("a" in this) { } else { let y = this.a; ~ diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js index 33e8eefa14650..643c73feb50a3 100644 --- a/tests/baselines/reference/inKeywordTypeguard.js +++ b/tests/baselines/reference/inKeywordTypeguard.js @@ -18,10 +18,10 @@ function positiveClassesTest(x: A | B) { } } -class AOpt { a?: string } -class BOpn { b?: string } +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } -function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { if ("a" in x) { x.a = "1"; } else { @@ -30,11 +30,11 @@ function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { } class AWithMethod { - a(): string { return "" } + a(): string { return ""; } } class BWithMethod { - b(): string { return "" } + b(): string { return ""; } } function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { @@ -55,8 +55,8 @@ function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWit } } -class C { a: string } -class D { a: string } +class C { a: string; } +class D { a: string; } function negativeMultipleClassesTest(x: A | B | C | D) { if ("a" in x) { @@ -66,9 +66,9 @@ function negativeMultipleClassesTest(x: A | B | C | D) { } } -class ClassWithProp { prop: A | B } +class ClassWithUnionProp { prop: A | B } -function negativePropTest(x: ClassWithProp) { +function negativePropTest(x: ClassWithUnionProp) { if ("a" in x.prop) { let y: string = x.prop.b; } else { @@ -79,7 +79,7 @@ function negativePropTest(x: ClassWithProp) { class NegativeClassTest { protected prop: A | B; inThis() { - if ('a' in this.prop) { + if ("a" in this.prop) { let z: number = this.prop.b; } else { let y: string = this.prop.a; @@ -90,7 +90,7 @@ class NegativeClassTest { class UnreachableCodeDetection { a: string; inThis() { - if ('a' in this) { + if ("a" in this) { } else { let y = this.a; } @@ -124,15 +124,15 @@ function positiveClassesTest(x) { x.a = "1"; } } -var AOpt = (function () { - function AOpt() { +var AWithOptionalProp = (function () { + function AWithOptionalProp() { } - return AOpt; + return AWithOptionalProp; }()); -var BOpn = (function () { - function BOpn() { +var BWithOptionalProp = (function () { + function BWithOptionalProp() { } - return BOpn; + return BWithOptionalProp; }()); function positiveTestClassesWithOptionalProperties(x) { if ("a" in x) { @@ -190,10 +190,10 @@ function negativeMultipleClassesTest(x) { x.a = "1"; } } -var ClassWithProp = (function () { - function ClassWithProp() { +var ClassWithUnionProp = (function () { + function ClassWithUnionProp() { } - return ClassWithProp; + return ClassWithUnionProp; }()); function negativePropTest(x) { if ("a" in x.prop) { @@ -207,7 +207,7 @@ var NegativeClassTest = (function () { function NegativeClassTest() { } NegativeClassTest.prototype.inThis = function () { - if ('a' in this.prop) { + if ("a" in this.prop) { var z = this.prop.b; } else { @@ -220,7 +220,7 @@ var UnreachableCodeDetection = (function () { function UnreachableCodeDetection() { } UnreachableCodeDetection.prototype.inThis = function () { - if ('a' in this) { + if ("a" in this) { } else { var y = this.a; diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js index 644abfaf33ff8..76b10066fdd5c 100644 --- a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js @@ -3,8 +3,6 @@ class A { a: string; } class B { b: number; } class C { b: Object; } class D { a: Date; } -class ClassWithProp { prop: A | B } -class NestedClassWithProp { outer: ClassWithProp } function namedClasses(x: A | B) { if ("a" in x) { @@ -29,6 +27,20 @@ function anonymousClasses(x: { a: string; } | { b: number; }) { let z: number = x.b; } } + +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + const y: string = x instanceof AWithOptionalProp + ? x.a + : x.b + } +} + function inParenthesizedExpression(x: A | B) { if ("a" in (x)) { let y: string = x.a; @@ -37,8 +49,9 @@ function inParenthesizedExpression(x: A | B) { } } +class ClassWithUnionProp { prop: A | B; } -function inProperty(x: ClassWithProp) { +function inProperty(x: ClassWithUnionProp) { if ("a" in x.prop) { let y: string = x.prop.a; } else { @@ -46,6 +59,7 @@ function inProperty(x: ClassWithProp) { } } +class NestedClassWithProp { outer: ClassWithUnionProp; } function innestedProperty(x: NestedClassWithProp) { if ("a" in x.outer.prop) { @@ -58,7 +72,7 @@ function innestedProperty(x: NestedClassWithProp) { class InMemberOfClass { protected prop: A | B; inThis() { - if ('a' in this.prop) { + if ("a" in this.prop) { let y: string = this.prop.a; } else { let z: number = this.prop.b; @@ -66,11 +80,11 @@ class InMemberOfClass { } } -//added for completeness +// added for completeness class SelfAssert { a: string; inThis() { - if ('a' in this) { + if ("a" in this) { let y: string = this.a; } else { } @@ -98,16 +112,6 @@ var D = (function () { } return D; }()); -var ClassWithProp = (function () { - function ClassWithProp() { - } - return ClassWithProp; -}()); -var NestedClassWithProp = (function () { - function NestedClassWithProp() { - } - return NestedClassWithProp; -}()); function namedClasses(x) { if ("a" in x) { x.a = "1"; @@ -132,6 +136,26 @@ function anonymousClasses(x) { var z = x.b; } } +var AWithOptionalProp = (function () { + function AWithOptionalProp() { + } + return AWithOptionalProp; +}()); +var BWithOptionalProp = (function () { + function BWithOptionalProp() { + } + return BWithOptionalProp; +}()); +function positiveTestClassesWithOptionalProperties(x) { + if ("a" in x) { + x.a = "1"; + } + else { + var y = x instanceof AWithOptionalProp + ? x.a + : x.b; + } +} function inParenthesizedExpression(x) { if ("a" in (x)) { var y = x.a; @@ -140,6 +164,11 @@ function inParenthesizedExpression(x) { var z = x.b; } } +var ClassWithUnionProp = (function () { + function ClassWithUnionProp() { + } + return ClassWithUnionProp; +}()); function inProperty(x) { if ("a" in x.prop) { var y = x.prop.a; @@ -148,6 +177,11 @@ function inProperty(x) { var z = x.prop.b; } } +var NestedClassWithProp = (function () { + function NestedClassWithProp() { + } + return NestedClassWithProp; +}()); function innestedProperty(x) { if ("a" in x.outer.prop) { var y = x.outer.prop.a; @@ -160,7 +194,7 @@ var InMemberOfClass = (function () { function InMemberOfClass() { } InMemberOfClass.prototype.inThis = function () { - if ('a' in this.prop) { + if ("a" in this.prop) { var y = this.prop.a; } else { @@ -169,12 +203,12 @@ var InMemberOfClass = (function () { }; return InMemberOfClass; }()); -//added for completeness +// added for completeness var SelfAssert = (function () { function SelfAssert() { } SelfAssert.prototype.inThis = function () { - if ('a' in this) { + if ("a" in this) { var y = this.a; } else { diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols index 0302635b558af..72fb3df2fc571 100644 --- a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.symbols @@ -17,234 +17,273 @@ class D { a: Date; } >a : Symbol(D.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) >Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) -class ClassWithProp { prop: A | B } ->ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) ->B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) - -class NestedClassWithProp { outer: ClassWithProp } ->NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 35)) ->outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) - function namedClasses(x: A | B) { ->namedClasses : Symbol(namedClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 50)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>namedClasses : Symbol(namedClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 22)) >A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) >B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) if ("a" in x) { ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 22)) x.a = "1"; >x.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 22)) >a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) } else { x.b = 1; >x.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 7, 22)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 22)) >b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) } } function multipleClasses(x: A | B | C | D) { ->multipleClasses : Symbol(multipleClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 1)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>multipleClasses : Symbol(multipleClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 11, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 25)) >A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) >B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) >C : Symbol(C, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 22)) >D : Symbol(D, Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 22)) if ("a" in x) { ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 25)) let y: string | Date = x.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 17, 11)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 11)) >Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >x.a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 25)) >a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 9)) } else { let z: number | Object = x.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 19, 11)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 17, 11)) >Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >x.b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 15, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 13, 25)) >b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9), Decl(typeGuardOfFromPropNameInUnionType.ts, 2, 9)) } } function anonymousClasses(x: { a: string; } | { b: number; }) { ->anonymousClasses : Symbol(anonymousClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 1)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) ->a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) ->b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) +>anonymousClasses : Symbol(anonymousClasses, Decl(typeGuardOfFromPropNameInUnionType.ts, 19, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 26)) +>a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 30)) +>b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 47)) if ("a" in x) { ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 26)) let y: string = x.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 25, 11)) ->x.a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) ->a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 30)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 11)) +>x.a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 30)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 26)) +>a : Symbol(a, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 30)) } else { let z: number = x.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 27, 11)) ->x.b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 26)) ->b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 23, 47)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 25, 11)) +>x.b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 47)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 26)) +>b : Symbol(b, Decl(typeGuardOfFromPropNameInUnionType.ts, 21, 47)) + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 27, 1)) +>a : Symbol(AWithOptionalProp.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 25)) + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 39)) +>b : Symbol(BWithOptionalProp.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 25)) + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : Symbol(positiveTestClassesWithOptionalProperties, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 39)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 27, 1)) +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 39)) + + if ("a" in x) { +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) + + x.a = "1"; +>x.a : Symbol(AWithOptionalProp.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) +>a : Symbol(AWithOptionalProp.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 25)) + + } else { + const y: string = x instanceof AWithOptionalProp +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 36, 13)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 27, 1)) + + ? x.a +>x.a : Symbol(AWithOptionalProp.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) +>a : Symbol(AWithOptionalProp.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 25)) + + : x.b +>x.b : Symbol(BWithOptionalProp.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 25)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 51)) +>b : Symbol(BWithOptionalProp.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 25)) } } + function inParenthesizedExpression(x: A | B) { ->inParenthesizedExpression : Symbol(inParenthesizedExpression, Decl(typeGuardOfFromPropNameInUnionType.ts, 29, 1)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>inParenthesizedExpression : Symbol(inParenthesizedExpression, Decl(typeGuardOfFromPropNameInUnionType.ts, 40, 1)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 42, 35)) >A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) >B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) if ("a" in (x)) { ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 42, 35)) let y: string = x.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 32, 11)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 44, 11)) >x.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 42, 35)) >a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) } else { let z: number = x.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 34, 11)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 46, 11)) >x.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 30, 35)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 42, 35)) >b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) } } +class ClassWithUnionProp { prop: A | B; } +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 1)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) +>B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) -function inProperty(x: ClassWithProp) { ->inProperty : Symbol(inProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 36, 1)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) ->ClassWithProp : Symbol(ClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 3, 20)) +function inProperty(x: ClassWithUnionProp) { +>inProperty : Symbol(inProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 41)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 20)) +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 1)) if ("a" in x.prop) { ->x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 20)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) let y: string = x.prop.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 41, 11)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 11)) >x.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) ->x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 20)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) >a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) } else { let z: number = x.prop.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 43, 11)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 11)) >x.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) ->x.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 39, 20)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 20)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) >b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) } } +class NestedClassWithProp { outer: ClassWithUnionProp; } +>NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 58, 1)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 1)) function innestedProperty(x: NestedClassWithProp) { ->innestedProperty : Symbol(innestedProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 45, 1)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) ->NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 35)) +>innestedProperty : Symbol(innestedProperty, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 56)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 26)) +>NestedClassWithProp : Symbol(NestedClassWithProp, Decl(typeGuardOfFromPropNameInUnionType.ts, 58, 1)) if ("a" in x.outer.prop) { ->x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) ->outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) let y: string = x.outer.prop.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 11)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 64, 11)) >x.outer.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) ->x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) ->outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) >a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) } else { let z: number = x.outer.prop.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 52, 11)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 66, 11)) >x.outer.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) ->x.outer.prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) ->x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 48, 26)) ->outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 5, 27)) ->prop : Symbol(ClassWithProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 4, 21)) +>x.outer.prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) +>x.outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>x : Symbol(x, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 26)) +>outer : Symbol(NestedClassWithProp.outer, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 27)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 50, 26)) >b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) } } class InMemberOfClass { ->InMemberOfClass : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) +>InMemberOfClass : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 1)) protected prop: A | B; ->prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) >A : Symbol(A, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 0)) >B : Symbol(B, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 22)) inThis() { ->inThis : Symbol(InMemberOfClass.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 57, 26)) +>inThis : Symbol(InMemberOfClass.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 71, 26)) - if ('a' in this.prop) { ->this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) ->this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) ->prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) + if ("a" in this.prop) { +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) let y: string = this.prop.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 60, 15)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 74, 15)) >this.prop.a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) ->this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) ->this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) ->prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) >a : Symbol(A.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 0, 9)) } else { let z: number = this.prop.b; ->z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 62, 15)) +>z : Symbol(z, Decl(typeGuardOfFromPropNameInUnionType.ts, 76, 15)) >this.prop.b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) ->this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) ->this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 54, 1)) ->prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 56, 23)) +>this.prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) +>this : Symbol(InMemberOfClass, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 1)) +>prop : Symbol(InMemberOfClass.prop, Decl(typeGuardOfFromPropNameInUnionType.ts, 70, 23)) >b : Symbol(B.b, Decl(typeGuardOfFromPropNameInUnionType.ts, 1, 9)) } } } -//added for completeness +// added for completeness class SelfAssert { ->SelfAssert : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) +>SelfAssert : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 79, 1)) a: string; ->a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) +>a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 82, 18)) inThis() { ->inThis : Symbol(SelfAssert.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 69, 14)) +>inThis : Symbol(SelfAssert.inThis, Decl(typeGuardOfFromPropNameInUnionType.ts, 83, 14)) - if ('a' in this) { ->this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) + if ("a" in this) { +>this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 79, 1)) let y: string = this.a; ->y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 72, 15)) ->this.a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) ->this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 65, 1)) ->a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 68, 18)) +>y : Symbol(y, Decl(typeGuardOfFromPropNameInUnionType.ts, 86, 15)) +>this.a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 82, 18)) +>this : Symbol(SelfAssert, Decl(typeGuardOfFromPropNameInUnionType.ts, 79, 1)) +>a : Symbol(SelfAssert.a, Decl(typeGuardOfFromPropNameInUnionType.ts, 82, 18)) } else { } diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types index d9984840fb3b1..4aff12ae07759 100644 --- a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.types @@ -17,17 +17,6 @@ class D { a: Date; } >a : Date >Date : Date -class ClassWithProp { prop: A | B } ->ClassWithProp : ClassWithProp ->prop : A | B ->A : A ->B : B - -class NestedClassWithProp { outer: ClassWithProp } ->NestedClassWithProp : NestedClassWithProp ->outer : ClassWithProp ->ClassWithProp : ClassWithProp - function namedClasses(x: A | B) { >namedClasses : (x: A | B) => void >x : A | B @@ -111,6 +100,53 @@ function anonymousClasses(x: { a: string; } | { b: number; }) { >b : number } } + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : AWithOptionalProp +>a : string + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : BWithOptionalProp +>b : string + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void +>x : AWithOptionalProp | BWithOptionalProp +>AWithOptionalProp : AWithOptionalProp +>BWithOptionalProp : BWithOptionalProp + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithOptionalProp | BWithOptionalProp + + x.a = "1"; +>x.a = "1" : "1" +>x.a : string +>x : AWithOptionalProp +>a : string +>"1" : "1" + + } else { + const y: string = x instanceof AWithOptionalProp +>y : string +>x instanceof AWithOptionalProp ? x.a : x.b : string +>x instanceof AWithOptionalProp : boolean +>x : AWithOptionalProp | BWithOptionalProp +>AWithOptionalProp : typeof AWithOptionalProp + + ? x.a +>x.a : string +>x : AWithOptionalProp +>a : string + + : x.b +>x.b : string +>x : BWithOptionalProp +>b : string + } +} + function inParenthesizedExpression(x: A | B) { >inParenthesizedExpression : (x: A | B) => void >x : A | B @@ -138,24 +174,29 @@ function inParenthesizedExpression(x: A | B) { } } +class ClassWithUnionProp { prop: A | B; } +>ClassWithUnionProp : ClassWithUnionProp +>prop : A | B +>A : A +>B : B -function inProperty(x: ClassWithProp) { ->inProperty : (x: ClassWithProp) => void ->x : ClassWithProp ->ClassWithProp : ClassWithProp +function inProperty(x: ClassWithUnionProp) { +>inProperty : (x: ClassWithUnionProp) => void +>x : ClassWithUnionProp +>ClassWithUnionProp : ClassWithUnionProp if ("a" in x.prop) { >"a" in x.prop : boolean >"a" : "a" >x.prop : A | B ->x : ClassWithProp +>x : ClassWithUnionProp >prop : A | B let y: string = x.prop.a; >y : string >x.prop.a : string >x.prop : A ->x : ClassWithProp +>x : ClassWithUnionProp >prop : A >a : string @@ -164,12 +205,16 @@ function inProperty(x: ClassWithProp) { >z : number >x.prop.b : number >x.prop : B ->x : ClassWithProp +>x : ClassWithUnionProp >prop : B >b : number } } +class NestedClassWithProp { outer: ClassWithUnionProp; } +>NestedClassWithProp : NestedClassWithProp +>outer : ClassWithUnionProp +>ClassWithUnionProp : ClassWithUnionProp function innestedProperty(x: NestedClassWithProp) { >innestedProperty : (x: NestedClassWithProp) => void @@ -180,18 +225,18 @@ function innestedProperty(x: NestedClassWithProp) { >"a" in x.outer.prop : boolean >"a" : "a" >x.outer.prop : A | B ->x.outer : ClassWithProp +>x.outer : ClassWithUnionProp >x : NestedClassWithProp ->outer : ClassWithProp +>outer : ClassWithUnionProp >prop : A | B let y: string = x.outer.prop.a; >y : string >x.outer.prop.a : string >x.outer.prop : A ->x.outer : ClassWithProp +>x.outer : ClassWithUnionProp >x : NestedClassWithProp ->outer : ClassWithProp +>outer : ClassWithUnionProp >prop : A >a : string @@ -200,9 +245,9 @@ function innestedProperty(x: NestedClassWithProp) { >z : number >x.outer.prop.b : number >x.outer.prop : B ->x.outer : ClassWithProp +>x.outer : ClassWithUnionProp >x : NestedClassWithProp ->outer : ClassWithProp +>outer : ClassWithUnionProp >prop : B >b : number } @@ -219,9 +264,9 @@ class InMemberOfClass { inThis() { >inThis : () => void - if ('a' in this.prop) { ->'a' in this.prop : boolean ->'a' : "a" + if ("a" in this.prop) { +>"a" in this.prop : boolean +>"a" : "a" >this.prop : A | B >this : this >prop : A | B @@ -246,7 +291,7 @@ class InMemberOfClass { } } -//added for completeness +// added for completeness class SelfAssert { >SelfAssert : SelfAssert @@ -256,9 +301,9 @@ class SelfAssert { inThis() { >inThis : () => void - if ('a' in this) { ->'a' in this : boolean ->'a' : "a" + if ("a" in this) { +>"a" in this : boolean +>"a" : "a" >this : this let y: string = this.a; diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index 90d1942e7c5a6..bbab20afc6f41 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -1,97 +1,97 @@ -class A { a: string; } -class B { b: string; } - -function negativeClassesTest(x: A | B) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -function positiveClassesTest(x: A | B) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -class AOpt { a?: string } -class BOpn { b?: string } - -function positiveTestClassesWithOptionalProperties(x: AOpt | BOpn) { - if ("a" in x) { - x.a = "1"; - } else { - x.b = "1"; - } -} - -class AWithMethod { - a(): string { return "" } -} - -class BWithMethod { - b(): string { return "" } -} - -function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { - if ("a" in x) { - x.a(); - x.b(); - } else { - } -} - -function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { - if ("c" in x) { - x.a(); - x.b(); - } else { - x.a(); - x.b(); - } -} - -class C { a: string } -class D { a: string } - -function negativeMultipleClassesTest(x: A | B | C | D) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -class ClassWithProp { prop: A | B } - -function negativePropTest(x: ClassWithProp) { - if ("a" in x.prop) { - let y: string = x.prop.b; - } else { - let z: string = x.prop.a; - } -} - -class NegativeClassTest { - protected prop: A | B; - inThis() { - if ('a' in this.prop) { - let z: number = this.prop.b; - } else { - let y: string = this.prop.a; - } - } -} - -class UnreachableCodeDetection { - a: string; - inThis() { - if ('a' in this) { - } else { - let y = this.a; - } - } +class A { a: string; } +class B { b: string; } + +function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } +} + +class AWithMethod { + a(): string { return ""; } +} + +class BWithMethod { + b(): string { return ""; } +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + x.b(); + } else { + x.a(); + x.b(); + } +} + +class C { a: string; } +class D { a: string; } + +function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class ClassWithUnionProp { prop: A | B } + +function negativePropTest(x: ClassWithUnionProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + } else { + let z: string = x.prop.a; + } +} + +class NegativeClassTest { + protected prop: A | B; + inThis() { + if ("a" in this.prop) { + let z: number = this.prop.b; + } else { + let y: string = this.prop.a; + } + } +} + +class UnreachableCodeDetection { + a: string; + inThis() { + if ("a" in this) { + } else { + let y = this.a; + } + } } \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts index 2a14fd2aea87a..d89d7b8c4efcb 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFromPropNameInUnionType.ts @@ -1,77 +1,91 @@ -class A { a: string; } -class B { b: number; } -class C { b: Object; } -class D { a: Date; } -class ClassWithProp { prop: A | B } -class NestedClassWithProp { outer: ClassWithProp } - -function namedClasses(x: A | B) { - if ("a" in x) { - x.a = "1"; - } else { - x.b = 1; - } -} - -function multipleClasses(x: A | B | C | D) { - if ("a" in x) { - let y: string | Date = x.a; - } else { - let z: number | Object = x.b; - } -} - -function anonymousClasses(x: { a: string; } | { b: number; }) { - if ("a" in x) { - let y: string = x.a; - } else { - let z: number = x.b; - } -} -function inParenthesizedExpression(x: A | B) { - if ("a" in (x)) { - let y: string = x.a; - } else { - let z: number = x.b; - } -} - - -function inProperty(x: ClassWithProp) { - if ("a" in x.prop) { - let y: string = x.prop.a; - } else { - let z: number = x.prop.b; - } -} - - -function innestedProperty(x: NestedClassWithProp) { - if ("a" in x.outer.prop) { - let y: string = x.outer.prop.a; - } else { - let z: number = x.outer.prop.b; - } -} - -class InMemberOfClass { - protected prop: A | B; - inThis() { - if ('a' in this.prop) { - let y: string = this.prop.a; - } else { - let z: number = this.prop.b; - } - } -} - -//added for completeness -class SelfAssert { - a: string; - inThis() { - if ('a' in this) { - let y: string = this.a; - } else { - } - } +class A { a: string; } +class B { b: number; } +class C { b: Object; } +class D { a: Date; } + +function namedClasses(x: A | B) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = 1; + } +} + +function multipleClasses(x: A | B | C | D) { + if ("a" in x) { + let y: string | Date = x.a; + } else { + let z: number | Object = x.b; + } +} + +function anonymousClasses(x: { a: string; } | { b: number; }) { + if ("a" in x) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} + +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + const y: string = x instanceof AWithOptionalProp + ? x.a + : x.b + } +} + +function inParenthesizedExpression(x: A | B) { + if ("a" in (x)) { + let y: string = x.a; + } else { + let z: number = x.b; + } +} + +class ClassWithUnionProp { prop: A | B; } + +function inProperty(x: ClassWithUnionProp) { + if ("a" in x.prop) { + let y: string = x.prop.a; + } else { + let z: number = x.prop.b; + } +} + +class NestedClassWithProp { outer: ClassWithUnionProp; } + +function innestedProperty(x: NestedClassWithProp) { + if ("a" in x.outer.prop) { + let y: string = x.outer.prop.a; + } else { + let z: number = x.outer.prop.b; + } +} + +class InMemberOfClass { + protected prop: A | B; + inThis() { + if ("a" in this.prop) { + let y: string = this.prop.a; + } else { + let z: number = this.prop.b; + } + } +} + +// added for completeness +class SelfAssert { + a: string; + inThis() { + if ("a" in this) { + let y: string = this.a; + } else { + } + } } \ No newline at end of file From 04b9b304a3afe17d8e24f09c6ddbb308ed99b6d7 Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Wed, 6 Dec 2017 01:39:34 +0300 Subject: [PATCH 4/4] Change isTypePresencePossible, accept baseline --- src/compiler/checker.ts | 24 +- .../reference/fixSignatureCaching.errors.txt | 5 +- .../reference/fixSignatureCaching.symbols | 2 - .../reference/fixSignatureCaching.types | 10 +- .../baselines/reference/inKeywordTypeguard.js | 22 +- .../reference/inKeywordTypeguard.symbols | 241 +++++++++++++ .../reference/inKeywordTypeguard.types | 318 ++++++++++++++++++ .../typeGuardOfFromPropNameInUnionType.js | 20 +- 8 files changed, 595 insertions(+), 47 deletions(-) create mode 100644 tests/baselines/reference/inKeywordTypeguard.symbols create mode 100644 tests/baselines/reference/inKeywordTypeguard.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f2db45bab262..5404a54975dce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12722,30 +12722,18 @@ namespace ts { return type; } - function isTypePresencePossible(type: Type, propName: string, shouldHaveProperty: boolean) { + function isTypePresencePossible(type: Type, propName: __String, assumeTrue: boolean) { const prop = getPropertyOfType(type, propName); - if (!prop) { - // if there is NO property: - // - but we assume type SHOULD have it then presence of object of following type IS NOT possible - // - and we assume type SHOULD NOT have it then presence of object of following type IS possible - return !shouldHaveProperty; - } else if (prop.flags & SymbolFlags.Optional) { - // if there is an optional property: - // - and we assume type SHOULD have it then presence of object of following type IS possible - // - but assume type SHOULD NOT have it then presence of object of following type IS still possible - return true; - } else /* if (prop.flags & SymbolFlags.Required) */ { - // if there is a required property: - // - and we assume type SHOULD have it then presence of object of following type IS possible - // - but we assume type SHOULD NOT have it then presence of object of following type IS NOT possible - return shouldHaveProperty; + if (prop) { + return (prop.flags & SymbolFlags.Optional) ? true : assumeTrue; } + return !assumeTrue; } function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { if ((type.flags & (TypeFlags.Union | TypeFlags.Object)) || (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType)) { - const propName = literal.text; - return filterType(type, t => isTypePresencePossible(t, propName, /* shouldHaveProperty */ assumeTrue)); + const propName = escapeLeadingUnderscores(literal.text); + return filterType(type, t => isTypePresencePossible(t, propName, /* assumeTrue */ assumeTrue)); } return type; } diff --git a/tests/baselines/reference/fixSignatureCaching.errors.txt b/tests/baselines/reference/fixSignatureCaching.errors.txt index 07d706a846f6e..e991908c850a3 100644 --- a/tests/baselines/reference/fixSignatureCaching.errors.txt +++ b/tests/baselines/reference/fixSignatureCaching.errors.txt @@ -3,6 +3,7 @@ tests/cases/conformance/fixSignatureCaching.ts(284,10): error TS2339: Property ' tests/cases/conformance/fixSignatureCaching.ts(293,10): error TS2339: Property 'FALLBACK_PHONE' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(294,10): error TS2339: Property 'FALLBACK_TABLET' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(295,10): error TS2339: Property 'FALLBACK_MOBILE' does not exist on type '{}'. +tests/cases/conformance/fixSignatureCaching.ts(301,17): error TS2339: Property 'isArray' does not exist on type 'never'. tests/cases/conformance/fixSignatureCaching.ts(330,74): error TS2339: Property 'mobileDetectRules' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(369,10): error TS2339: Property 'findMatch' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(387,10): error TS2339: Property 'findMatches' does not exist on type '{}'. @@ -71,7 +72,7 @@ tests/cases/conformance/fixSignatureCaching.ts(982,23): error TS2304: Cannot fin tests/cases/conformance/fixSignatureCaching.ts(983,37): error TS2304: Cannot find name 'window'. -==== tests/cases/conformance/fixSignatureCaching.ts (71 errors) ==== +==== tests/cases/conformance/fixSignatureCaching.ts (72 errors) ==== // Repro from #10697 (function (define, undefined) { @@ -383,6 +384,8 @@ tests/cases/conformance/fixSignatureCaching.ts(983,37): error TS2304: Cannot fin isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray; + ~~~~~~~ +!!! error TS2339: Property 'isArray' does not exist on type 'never'. function equalIC(a, b) { return a != null && b != null && a.toLowerCase() === b.toLowerCase(); diff --git a/tests/baselines/reference/fixSignatureCaching.symbols b/tests/baselines/reference/fixSignatureCaching.symbols index 9bae31520fd17..87cf8d0c44e14 100644 --- a/tests/baselines/reference/fixSignatureCaching.symbols +++ b/tests/baselines/reference/fixSignatureCaching.symbols @@ -358,9 +358,7 @@ define(function () { >value : Symbol(value, Decl(fixSignatureCaching.ts, 299, 20)) : Array.isArray; ->Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.d.ts, --, --)) >Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->isArray : Symbol(ArrayConstructor.isArray, Decl(lib.d.ts, --, --)) function equalIC(a, b) { >equalIC : Symbol(equalIC, Decl(fixSignatureCaching.ts, 300, 24)) diff --git a/tests/baselines/reference/fixSignatureCaching.types b/tests/baselines/reference/fixSignatureCaching.types index 61d195e2155f8..60502cae58395 100644 --- a/tests/baselines/reference/fixSignatureCaching.types +++ b/tests/baselines/reference/fixSignatureCaching.types @@ -893,9 +893,9 @@ define(function () { >'[object Array]' : "[object Array]" isArray = 'isArray' in Array ->isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean +>isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any >isArray : any ->'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean +>'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any >'isArray' in Array : boolean >'isArray' : "isArray" >Array : ArrayConstructor @@ -916,9 +916,9 @@ define(function () { >'[object Array]' : "[object Array]" : Array.isArray; ->Array.isArray : (arg: any) => arg is any[] ->Array : ArrayConstructor ->isArray : (arg: any) => arg is any[] +>Array.isArray : any +>Array : never +>isArray : any function equalIC(a, b) { >equalIC : (a: any, b: any) => boolean diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js index 643c73feb50a3..41ad27f96dff5 100644 --- a/tests/baselines/reference/inKeywordTypeguard.js +++ b/tests/baselines/reference/inKeywordTypeguard.js @@ -98,12 +98,12 @@ class UnreachableCodeDetection { } //// [inKeywordTypeguard.js] -var A = (function () { +var A = /** @class */ (function () { function A() { } return A; }()); -var B = (function () { +var B = /** @class */ (function () { function B() { } return B; @@ -124,12 +124,12 @@ function positiveClassesTest(x) { x.a = "1"; } } -var AWithOptionalProp = (function () { +var AWithOptionalProp = /** @class */ (function () { function AWithOptionalProp() { } return AWithOptionalProp; }()); -var BWithOptionalProp = (function () { +var BWithOptionalProp = /** @class */ (function () { function BWithOptionalProp() { } return BWithOptionalProp; @@ -142,13 +142,13 @@ function positiveTestClassesWithOptionalProperties(x) { x.b = "1"; } } -var AWithMethod = (function () { +var AWithMethod = /** @class */ (function () { function AWithMethod() { } AWithMethod.prototype.a = function () { return ""; }; return AWithMethod; }()); -var BWithMethod = (function () { +var BWithMethod = /** @class */ (function () { function BWithMethod() { } BWithMethod.prototype.b = function () { return ""; }; @@ -172,12 +172,12 @@ function negativeTestClassesWithMemberMissingInBothClasses(x) { x.b(); } } -var C = (function () { +var C = /** @class */ (function () { function C() { } return C; }()); -var D = (function () { +var D = /** @class */ (function () { function D() { } return D; @@ -190,7 +190,7 @@ function negativeMultipleClassesTest(x) { x.a = "1"; } } -var ClassWithUnionProp = (function () { +var ClassWithUnionProp = /** @class */ (function () { function ClassWithUnionProp() { } return ClassWithUnionProp; @@ -203,7 +203,7 @@ function negativePropTest(x) { var z = x.prop.a; } } -var NegativeClassTest = (function () { +var NegativeClassTest = /** @class */ (function () { function NegativeClassTest() { } NegativeClassTest.prototype.inThis = function () { @@ -216,7 +216,7 @@ var NegativeClassTest = (function () { }; return NegativeClassTest; }()); -var UnreachableCodeDetection = (function () { +var UnreachableCodeDetection = /** @class */ (function () { function UnreachableCodeDetection() { } UnreachableCodeDetection.prototype.inThis = function () { diff --git a/tests/baselines/reference/inKeywordTypeguard.symbols b/tests/baselines/reference/inKeywordTypeguard.symbols new file mode 100644 index 0000000000000..c73f6f4901de5 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard.symbols @@ -0,0 +1,241 @@ +=== tests/cases/compiler/inKeywordTypeguard.ts === +class A { a: string; } +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>a : Symbol(A.a, Decl(inKeywordTypeguard.ts, 0, 9)) + +class B { b: string; } +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) +>b : Symbol(B.b, Decl(inKeywordTypeguard.ts, 1, 9)) + +function negativeClassesTest(x: A | B) { +>negativeClassesTest : Symbol(negativeClassesTest, Decl(inKeywordTypeguard.ts, 1, 22)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + } +} + +function positiveClassesTest(x: A | B) { +>positiveClassesTest : Symbol(positiveClassesTest, Decl(inKeywordTypeguard.ts, 9, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1)) +>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39)) +>b : Symbol(BWithOptionalProp.b, Decl(inKeywordTypeguard.ts, 20, 25)) + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : Symbol(positiveTestClassesWithOptionalProperties, Decl(inKeywordTypeguard.ts, 20, 39)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1)) +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) + + x.a = "1"; +>x.a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) +>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) + + } else { + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) + } +} + +class AWithMethod { +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) + + a(): string { return ""; } +>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) +} + +class BWithMethod { +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + b(): string { return ""; } +>b : Symbol(BWithMethod.b, Decl(inKeywordTypeguard.ts, 34, 19)) +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMembers : Symbol(negativeTestClassesWithMembers, Decl(inKeywordTypeguard.ts, 36, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) + + x.a(); +>x.a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) +>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) + + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMemberMissingInBothClasses : Symbol(negativeTestClassesWithMemberMissingInBothClasses, Decl(inKeywordTypeguard.ts, 44, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + if ("c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.a(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + } else { + x.a(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + } +} + +class C { a: string; } +>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1)) +>a : Symbol(C.a, Decl(inKeywordTypeguard.ts, 56, 9)) + +class D { a: string; } +>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22)) +>a : Symbol(D.a, Decl(inKeywordTypeguard.ts, 57, 9)) + +function negativeMultipleClassesTest(x: A | B | C | D) { +>negativeMultipleClassesTest : Symbol(negativeMultipleClassesTest, Decl(inKeywordTypeguard.ts, 57, 22)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) +>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1)) +>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + } +} + +class ClassWithUnionProp { prop: A | B } +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + +function negativePropTest(x: ClassWithUnionProp) { +>negativePropTest : Symbol(negativePropTest, Decl(inKeywordTypeguard.ts, 67, 40)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1)) + + if ("a" in x.prop) { +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + + let y: string = x.prop.b; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 71, 11)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + + } else { + let z: string = x.prop.a; +>z : Symbol(z, Decl(inKeywordTypeguard.ts, 73, 11)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + } +} + +class NegativeClassTest { +>NegativeClassTest : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) + + protected prop: A | B; +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + inThis() { +>inThis : Symbol(NegativeClassTest.inThis, Decl(inKeywordTypeguard.ts, 78, 26)) + + if ("a" in this.prop) { +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + + let z: number = this.prop.b; +>z : Symbol(z, Decl(inKeywordTypeguard.ts, 81, 15)) +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + + } else { + let y: string = this.prop.a; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 83, 15)) +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + } + } +} + +class UnreachableCodeDetection { +>UnreachableCodeDetection : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1)) + + a: string; +>a : Symbol(UnreachableCodeDetection.a, Decl(inKeywordTypeguard.ts, 88, 32)) + + inThis() { +>inThis : Symbol(UnreachableCodeDetection.inThis, Decl(inKeywordTypeguard.ts, 89, 14)) + + if ("a" in this) { +>this : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1)) + + } else { + let y = this.a; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 93, 15)) + } + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types new file mode 100644 index 0000000000000..3f9e8fd063869 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard.types @@ -0,0 +1,318 @@ +=== tests/cases/compiler/inKeywordTypeguard.ts === +class A { a: string; } +>A : A +>a : string + +class B { b: string; } +>B : B +>b : string + +function negativeClassesTest(x: A | B) { +>negativeClassesTest : (x: A | B) => void +>x : A | B +>A : A +>B : B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +function positiveClassesTest(x: A | B) { +>positiveClassesTest : (x: A | B) => void +>x : A | B +>A : A +>B : B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : AWithOptionalProp +>a : string + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : BWithOptionalProp +>b : string + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void +>x : AWithOptionalProp | BWithOptionalProp +>AWithOptionalProp : AWithOptionalProp +>BWithOptionalProp : BWithOptionalProp + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithOptionalProp | BWithOptionalProp + + x.a = "1"; +>x.a = "1" : "1" +>x.a : string +>x : AWithOptionalProp +>a : string +>"1" : "1" + + } else { + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : AWithOptionalProp | BWithOptionalProp +>b : any +>"1" : "1" + } +} + +class AWithMethod { +>AWithMethod : AWithMethod + + a(): string { return ""; } +>a : () => string +>"" : "" +} + +class BWithMethod { +>BWithMethod : BWithMethod + + b(): string { return ""; } +>b : () => string +>"" : "" +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod +>AWithMethod : AWithMethod +>BWithMethod : BWithMethod + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : string +>x.a : () => string +>x : AWithMethod +>a : () => string + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod +>b : any + + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod +>AWithMethod : AWithMethod +>BWithMethod : BWithMethod + + if ("c" in x) { +>"c" in x : boolean +>"c" : "c" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : any +>x.a : any +>x : never +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : never +>b : any + + } else { + x.a(); +>x.a() : any +>x.a : any +>x : AWithMethod | BWithMethod +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod | BWithMethod +>b : any + } +} + +class C { a: string; } +>C : C +>a : string + +class D { a: string; } +>D : D +>a : string + +function negativeMultipleClassesTest(x: A | B | C | D) { +>negativeMultipleClassesTest : (x: A | B | C | D) => void +>x : A | B | C | D +>A : A +>B : B +>C : C +>D : D + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B | C | D + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A | C | D +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class ClassWithUnionProp { prop: A | B } +>ClassWithUnionProp : ClassWithUnionProp +>prop : A | B +>A : A +>B : B + +function negativePropTest(x: ClassWithUnionProp) { +>negativePropTest : (x: ClassWithUnionProp) => void +>x : ClassWithUnionProp +>ClassWithUnionProp : ClassWithUnionProp + + if ("a" in x.prop) { +>"a" in x.prop : boolean +>"a" : "a" +>x.prop : A | B +>x : ClassWithUnionProp +>prop : A | B + + let y: string = x.prop.b; +>y : string +>x.prop.b : any +>x.prop : A +>x : ClassWithUnionProp +>prop : A +>b : any + + } else { + let z: string = x.prop.a; +>z : string +>x.prop.a : any +>x.prop : B +>x : ClassWithUnionProp +>prop : B +>a : any + } +} + +class NegativeClassTest { +>NegativeClassTest : NegativeClassTest + + protected prop: A | B; +>prop : A | B +>A : A +>B : B + + inThis() { +>inThis : () => void + + if ("a" in this.prop) { +>"a" in this.prop : boolean +>"a" : "a" +>this.prop : A | B +>this : this +>prop : A | B + + let z: number = this.prop.b; +>z : number +>this.prop.b : any +>this.prop : A +>this : this +>prop : A +>b : any + + } else { + let y: string = this.prop.a; +>y : string +>this.prop.a : any +>this.prop : B +>this : this +>prop : B +>a : any + } + } +} + +class UnreachableCodeDetection { +>UnreachableCodeDetection : UnreachableCodeDetection + + a: string; +>a : string + + inThis() { +>inThis : () => void + + if ("a" in this) { +>"a" in this : boolean +>"a" : "a" +>this : this + + } else { + let y = this.a; +>y : any +>this.a : any +>this : never +>a : any + } + } +} diff --git a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js index 76b10066fdd5c..a2579328646e6 100644 --- a/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js +++ b/tests/baselines/reference/typeGuardOfFromPropNameInUnionType.js @@ -92,22 +92,22 @@ class SelfAssert { } //// [typeGuardOfFromPropNameInUnionType.js] -var A = (function () { +var A = /** @class */ (function () { function A() { } return A; }()); -var B = (function () { +var B = /** @class */ (function () { function B() { } return B; }()); -var C = (function () { +var C = /** @class */ (function () { function C() { } return C; }()); -var D = (function () { +var D = /** @class */ (function () { function D() { } return D; @@ -136,12 +136,12 @@ function anonymousClasses(x) { var z = x.b; } } -var AWithOptionalProp = (function () { +var AWithOptionalProp = /** @class */ (function () { function AWithOptionalProp() { } return AWithOptionalProp; }()); -var BWithOptionalProp = (function () { +var BWithOptionalProp = /** @class */ (function () { function BWithOptionalProp() { } return BWithOptionalProp; @@ -164,7 +164,7 @@ function inParenthesizedExpression(x) { var z = x.b; } } -var ClassWithUnionProp = (function () { +var ClassWithUnionProp = /** @class */ (function () { function ClassWithUnionProp() { } return ClassWithUnionProp; @@ -177,7 +177,7 @@ function inProperty(x) { var z = x.prop.b; } } -var NestedClassWithProp = (function () { +var NestedClassWithProp = /** @class */ (function () { function NestedClassWithProp() { } return NestedClassWithProp; @@ -190,7 +190,7 @@ function innestedProperty(x) { var z = x.outer.prop.b; } } -var InMemberOfClass = (function () { +var InMemberOfClass = /** @class */ (function () { function InMemberOfClass() { } InMemberOfClass.prototype.inThis = function () { @@ -204,7 +204,7 @@ var InMemberOfClass = (function () { return InMemberOfClass; }()); // added for completeness -var SelfAssert = (function () { +var SelfAssert = /** @class */ (function () { function SelfAssert() { } SelfAssert.prototype.inThis = function () {