From 9c3c2adfb0b9fd515af2c78109bcfdb139475549 Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Wed, 19 Apr 2017 00:08:54 +0300 Subject: [PATCH 1/8] 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 71b5e1afbff..4eab97f28bb 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 25d967795a7..7f7a75a33f9 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 00000000000..4ee09e71b60 --- /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 00000000000..33e8eefa146 --- /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 00000000000..644abfaf33f --- /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 00000000000..0302635b558 --- /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 00000000000..d9984840fb3 --- /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 00000000000..90d1942e7c5 --- /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 00000000000..2a14fd2aea8 --- /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/8] 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 7f7a75a33f9..1fc35e174dc 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/8] 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 1fc35e174dc..7f2db45bab2 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 4ee09e71b60..914daf42a7e 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 33e8eefa146..643c73feb50 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 644abfaf33f..76b10066fdd 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 0302635b558..72fb3df2fc5 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 d9984840fb3..4aff12ae077 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 90d1942e7c5..bbab20afc6f 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 2a14fd2aea8..d89d7b8c4ef 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 3a3bb8e3f07a1dac3e1297d08e2733eb7e7347d8 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 5 Dec 2017 13:19:01 -0800 Subject: [PATCH 4/8] Immediately respond to `applyCodeActionCommand` requests (#20448) --- src/server/session.ts | 17 ++++++++--------- .../reference/api/tsserverlibrary.d.ts | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index e09701617b8..0eab6c9732a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1571,15 +1571,15 @@ namespace ts.server { } } - private applyCodeActionCommand(commandName: string, requestSeq: number, args: protocol.ApplyCodeActionCommandRequestArgs): void { + private applyCodeActionCommand(args: protocol.ApplyCodeActionCommandRequestArgs): {} { const commands = args.command as CodeActionCommand | CodeActionCommand[]; // They should be sending back the command we sent them. for (const command of toArray(commands)) { const { project } = this.getFileAndProject(command); - const output = (success: boolean, message: string) => this.doOutput({}, commandName, requestSeq, success, message); project.getLanguageService().applyCodeActionCommand(command).then( - result => { output(/*success*/ true, result.successMessage); }, - error => { output(/*success*/ false, error); }); + _result => { /* TODO: GH#20447 report success message? */ }, + _error => { /* TODO: GH#20447 report errors */ }); } + return {}; } private getStartAndEndPosition(args: protocol.FileRangeRequestArgs, scriptInfo: ScriptInfo) { @@ -1705,17 +1705,17 @@ namespace ts.server { private handlers = createMapFromTemplate<(request: protocol.Request) => HandlerResponse>({ [CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => { this.projectService.openExternalProject(request.arguments, /*suppressRefreshOfInferredProjects*/ false); - // TODO: report errors + // TODO: GH#20447 report errors return this.requiredResponse(/*response*/ true); }, [CommandNames.OpenExternalProjects]: (request: protocol.OpenExternalProjectsRequest) => { this.projectService.openExternalProjects(request.arguments.projects); - // TODO: report errors + // TODO: GH#20447 report errors return this.requiredResponse(/*response*/ true); }, [CommandNames.CloseExternalProject]: (request: protocol.CloseExternalProjectRequest) => { this.projectService.closeExternalProject(request.arguments.projectFileName); - // TODO: report errors + // TODO: GH#20447 report errors return this.requiredResponse(/*response*/ true); }, [CommandNames.SynchronizeProjectList]: (request: protocol.SynchronizeProjectListRequest) => { @@ -1957,8 +1957,7 @@ namespace ts.server { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ false)); }, [CommandNames.ApplyCodeActionCommand]: (request: protocol.ApplyCodeActionCommandRequest) => { - this.applyCodeActionCommand(request.command, request.seq, request.arguments); - return this.notRequired(); // Response will come asynchronously. + return this.requiredResponse(this.applyCodeActionCommand(request.arguments)); }, [CommandNames.GetSupportedCodeFixes]: () => { return this.requiredResponse(this.getSupportedCodeFixes()); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c127232c099..abd03c26b0b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7072,7 +7072,7 @@ declare namespace ts.server { private getApplicableRefactors(args); private getEditsForRefactor(args, simplifiedResult); private getCodeFixes(args, simplifiedResult); - private applyCodeActionCommand(commandName, requestSeq, args); + private applyCodeActionCommand(args); private getStartAndEndPosition(args, scriptInfo); private mapCodeAction({description, changes: unmappedChanges, commands}, scriptInfo); private mapTextChangesToCodeEdits(project, textChanges); From 04b9b304a3afe17d8e24f09c6ddbb308ed99b6d7 Mon Sep 17 00:00:00 2001 From: IdeaHunter Date: Wed, 6 Dec 2017 01:39:34 +0300 Subject: [PATCH 5/8] 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 7f2db45bab2..5404a54975d 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 07d706a846f..e991908c850 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 9bae31520fd..87cf8d0c44e 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 61d195e2155..60502cae583 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 643c73feb50..41ad27f96df 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 00000000000..c73f6f4901d --- /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 00000000000..3f9e8fd0638 --- /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 76b10066fdd..a2579328646 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 () { From ae25d0976185d0761f0bedeabd6953ad238661eb Mon Sep 17 00:00:00 2001 From: "wenlu.wang" <805037171@163.com> Date: Wed, 6 Dec 2017 23:01:37 +0800 Subject: [PATCH 6/8] add supports of completion label list (#20362) --- src/services/completions.ts | 43 ++++++++++++++++++ .../fourslash/completionListWithLabel.ts | 44 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/cases/fourslash/completionListWithLabel.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 28837bcce40..6ea177cb70d 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -39,6 +39,11 @@ namespace ts.Completions { return getStringLiteralCompletionEntries(sourceFile, position, typeChecker, compilerOptions, host, log); } + const contextToken = findPrecedingToken(position, sourceFile); + if (isInBreakOrContinue(contextToken)) { + return getLabelCompletionAtPosition(contextToken); + } + const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, options, compilerOptions.target); if (!completionData) { return undefined; @@ -223,6 +228,18 @@ namespace ts.Completions { return uniques; } + function isInBreakOrContinue(contextToken: Node): boolean { + return contextToken && isBreakOrContinueStatement(contextToken.parent) && + (contextToken.kind === SyntaxKind.BreakKeyword || contextToken.kind === SyntaxKind.ContinueKeyword || contextToken.kind === SyntaxKind.Identifier); + } + + function getLabelCompletionAtPosition(contextToken: Node): CompletionInfo | undefined { + const entries = getLabelStatementCompletions(contextToken.parent); + if (entries.length) { + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; + } + } + function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost, log: Log): CompletionInfo | undefined { const node = findPrecedingToken(position, sourceFile); if (!node || node.kind !== SyntaxKind.StringLiteral) { @@ -358,6 +375,32 @@ namespace ts.Completions { return undefined; } + function getLabelStatementCompletions(node: Node): CompletionEntry[] { + const entries: CompletionEntry[] = []; + const uniques = createMap(); + let current = node; + + while (current) { + if (isFunctionLike(current)) { + break; + } + if (isLabeledStatement(current)) { + const name = current.label.text; + if (!uniques.has(name)) { + uniques.set(name, true); + entries.push({ + name, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.label, + sortText: "0" + }); + } + } + current = current.parent; + } + return entries; + } + function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker, uniques = createMap()): void { if (type && type.flags & TypeFlags.TypeParameter) { type = typeChecker.getBaseConstraintOfType(type); diff --git a/tests/cases/fourslash/completionListWithLabel.ts b/tests/cases/fourslash/completionListWithLabel.ts new file mode 100644 index 00000000000..1efc0ebb5fa --- /dev/null +++ b/tests/cases/fourslash/completionListWithLabel.ts @@ -0,0 +1,44 @@ +/// + +//// label: while (true) { +//// break /*1*/ +//// continue /*2*/ +//// testlabel: while (true) { +//// break /*3*/ +//// continue /*4*/ +//// break tes/*5*/ +//// continue tes/*6*/ +//// } +//// break /*7*/ +//// break; /*8*/ +////} + +goTo.marker("1"); +verify.completionListContains("label"); + +goTo.marker("2"); +verify.completionListContains("label"); +verify.not.completionListContains("testlabel"); + +goTo.marker("3"); +verify.completionListContains("label"); +verify.completionListContains("testlabel"); + +goTo.marker("4"); +verify.completionListContains("label"); +verify.completionListContains("testlabel"); + +goTo.marker("5"); +verify.completionListContains("testlabel"); +verify.completionListContains("label"); + +goTo.marker("6"); +verify.completionListContains("testlabel"); +verify.completionListContains("label"); + +goTo.marker("7"); +verify.completionListContains("label"); +verify.not.completionListContains("testlabel"); + +goTo.marker("8"); +verify.not.completionListContains("label"); From 8dca43173306f7bb4d7ac603b8b44dbb4e2adf26 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 6 Dec 2017 07:02:27 -0800 Subject: [PATCH 7/8] Use InternalSymbolName.Default more (#20480) --- src/compiler/checker.ts | 12 ++++++------ src/services/codefixes/importFixes.ts | 2 +- src/services/completions.ts | 10 ++++++---- src/services/importTracker.ts | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 25d967795a7..7cac2d50699 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1020,7 +1020,7 @@ namespace ts { // It's an external module. First see if the module has an export default and if the local // name of that export default matches. - if (result = moduleExports.get("default" as __String)) { + if (result = moduleExports.get(InternalSymbolName.Default)) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) { break loop; @@ -1474,8 +1474,8 @@ namespace ts { else { const exportValue = moduleSymbol.exports.get("export=" as __String); exportDefaultSymbol = exportValue - ? getPropertyOfType(getTypeOfSymbol(exportValue), "default" as __String) - : resolveSymbol(moduleSymbol.exports.get("default" as __String), dontResolveAlias); + ? getPropertyOfType(getTypeOfSymbol(exportValue), InternalSymbolName.Default) + : resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.Default), dontResolveAlias); } if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { @@ -1564,7 +1564,7 @@ namespace ts { symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); let symbolFromModule = getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias); // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default - if (!symbolFromModule && allowSyntheticDefaultImports && name.escapedText === "default") { + if (!symbolFromModule && allowSyntheticDefaultImports && name.escapedText === InternalSymbolName.Default) { symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); } const symbol = symbolFromModule && symbolFromVariable ? @@ -1951,7 +1951,7 @@ namespace ts { function extendExportSymbols(target: SymbolTable, source: SymbolTable | undefined, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) { if (!source) return; source.forEach((sourceSymbol, id) => { - if (id === "default") return; + if (id === InternalSymbolName.Default) return; const targetSymbol = target.get(id); if (!targetSymbol) { @@ -3221,7 +3221,7 @@ namespace ts { * ensuring that any names written with literals use element accesses. */ function appendPropertyOrElementAccessForSymbol(symbol: Symbol, writer: SymbolWriter): void { - const symbolName = symbol.escapedName === "default" ? "default" : getNameOfSymbolAsWritten(symbol); + const symbolName = symbol.escapedName === InternalSymbolName.Default ? InternalSymbolName.Default : getNameOfSymbolAsWritten(symbol); const firstChar = symbolName.charCodeAt(0); const needsElementAccess = !isIdentifierStart(firstChar, languageVersion); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 7bf057a03d5..4f422b10036 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -746,7 +746,7 @@ namespace ts.codefix { forEachExternalModuleToImportFrom(checker, sourceFile, allSourceFiles, moduleSymbol => { cancellationToken.throwIfCancellationRequested(); // check the default export - const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol); + const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol); if (defaultExport) { const localSymbol = getLocalSymbolForExportDefault(defaultExport); if ((localSymbol && localSymbol.escapedName === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, context.compilerOptions.target) === symbolName) diff --git a/src/services/completions.ts b/src/services/completions.ts index 6ea177cb70d..a340f8385e9 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -459,7 +459,9 @@ namespace ts.Completions { } function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string { - return origin && origin.isDefaultExport && symbol.name === "default" ? codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) : symbol.name; + return origin && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default + ? codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) + : symbol.name; } export interface CompletionEntryIdentifier { @@ -1119,7 +1121,7 @@ namespace ts.Completions { continue; } - const isDefaultExport = name === "default"; + const isDefaultExport = name === InternalSymbolName.Default; if (isDefaultExport) { const localSymbol = getLocalSymbolForExportDefault(symbol); if (localSymbol) { @@ -1799,10 +1801,10 @@ namespace ts.Completions { } if (existingImportsOrExports.size === 0) { - return filter(exportsOfModule, e => e.escapedName !== "default"); + return filter(exportsOfModule, e => e.escapedName !== InternalSymbolName.Default); } - return filter(exportsOfModule, e => e.escapedName !== "default" && !existingImportsOrExports.get(e.escapedName)); + return filter(exportsOfModule, e => e.escapedName !== InternalSymbolName.Default && !existingImportsOrExports.get(e.escapedName)); } /** diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index f12df4613c0..0432c342c8f 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -290,7 +290,7 @@ namespace ts.FindAllReferences { function isNameMatch(name: __String): boolean { // Use name of "default" even in `export =` case because we may have allowSyntheticDefaultImports - return name === exportSymbol.escapedName || exportKind !== ExportKind.Named && name === "default"; + return name === exportSymbol.escapedName || exportKind !== ExportKind.Named && name === InternalSymbolName.Default; } } @@ -534,7 +534,7 @@ namespace ts.FindAllReferences { // If `importedName` is undefined, do continue searching as the export is anonymous. // (All imports returned from this function will be ignored anyway if we are in rename and this is a not a named export.) const importedName = symbolName(importedSymbol); - if (importedName === undefined || importedName === "default" || importedName === symbol.escapedName) { + if (importedName === undefined || importedName === InternalSymbolName.Default || importedName === symbol.escapedName) { return { kind: ImportExport.Import, symbol: importedSymbol, ...isImport }; } } @@ -604,7 +604,7 @@ namespace ts.FindAllReferences { } function symbolName(symbol: Symbol): __String | undefined { - if (symbol.escapedName !== "default") { + if (symbol.escapedName !== InternalSymbolName.Default) { return symbol.escapedName; } From 06aeab8f311b8917d1a13eaf7caa442ca3945be6 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 6 Dec 2017 08:34:54 -0800 Subject: [PATCH 8/8] Minor cleanup for label completions (#20502) --- src/services/completions.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index a340f8385e9..8de32b95225 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -40,8 +40,9 @@ namespace ts.Completions { } const contextToken = findPrecedingToken(position, sourceFile); - if (isInBreakOrContinue(contextToken)) { - return getLabelCompletionAtPosition(contextToken); + if (contextToken && isBreakOrContinueStatement(contextToken.parent) + && (contextToken.kind === SyntaxKind.BreakKeyword || contextToken.kind === SyntaxKind.ContinueKeyword || contextToken.kind === SyntaxKind.Identifier)) { + return getLabelCompletionAtPosition(contextToken.parent); } const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, options, compilerOptions.target); @@ -228,13 +229,8 @@ namespace ts.Completions { return uniques; } - function isInBreakOrContinue(contextToken: Node): boolean { - return contextToken && isBreakOrContinueStatement(contextToken.parent) && - (contextToken.kind === SyntaxKind.BreakKeyword || contextToken.kind === SyntaxKind.ContinueKeyword || contextToken.kind === SyntaxKind.Identifier); - } - - function getLabelCompletionAtPosition(contextToken: Node): CompletionInfo | undefined { - const entries = getLabelStatementCompletions(contextToken.parent); + function getLabelCompletionAtPosition(node: BreakOrContinueStatement): CompletionInfo | undefined { + const entries = getLabelStatementCompletions(node); if (entries.length) { return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; }