From 3e3b80826fe26d1306cd1c5e9de18def227a8ec3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 29 Jul 2016 07:54:19 -0700 Subject: [PATCH 1/5] Properly reset type guards in loops --- src/compiler/checker.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04c12eb613c5d..691d74518959b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7764,16 +7764,17 @@ namespace ts { } function isMatchingReference(source: Node, target: Node): boolean { - if (source.kind === target.kind) { - switch (source.kind) { - case SyntaxKind.Identifier: - return getResolvedSymbol(source) === getResolvedSymbol(target); - case SyntaxKind.ThisKeyword: - return true; - case SyntaxKind.PropertyAccessExpression: - return (source).name.text === (target).name.text && - isMatchingReference((source).expression, (target).expression); - } + switch (source.kind) { + case SyntaxKind.Identifier: + return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || + (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && + getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target); + case SyntaxKind.ThisKeyword: + return target.kind === SyntaxKind.ThisKeyword; + case SyntaxKind.PropertyAccessExpression: + return target.kind === SyntaxKind.PropertyAccessExpression && + (source).name.text === (target).name.text && + isMatchingReference((source).expression, (target).expression); } return false; } @@ -8031,6 +8032,12 @@ namespace ts { getInitialTypeOfBindingElement(node); } + function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) { + return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ? + getInitialType(node) : + getAssignedType(node); + } + function getReferenceCandidate(node: Expression): Expression { switch (node.kind) { case SyntaxKind.ParenthesizedExpression: @@ -8153,19 +8160,9 @@ namespace ts { const node = flow.node; // Assignments only narrow the computed type if the declared type is a union type. Thus, we // only need to evaluate the assigned type if the declared type is a union type. - if ((node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) && - reference.kind === SyntaxKind.Identifier && - getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(reference)) === getSymbolOfNode(node)) { - return declaredType.flags & TypeFlags.Union ? - getAssignmentReducedType(declaredType, getInitialType(node)) : - declaredType; - } - // If the node is not a variable declaration or binding element, it is an identifier - // or a dotted name that is the target of an assignment. If we have a match, reduce - // the declared type by the assigned type. if (isMatchingReference(reference, node)) { return declaredType.flags & TypeFlags.Union ? - getAssignmentReducedType(declaredType, getAssignedType(node)) : + getAssignmentReducedType(declaredType, getInitialOrAssignedType(node)) : declaredType; } // We didn't have a direct match. However, if the reference is a dotted name, this From d9b67231e125b5dcdec526a17cf5be48b28b78c8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 29 Jul 2016 07:54:40 -0700 Subject: [PATCH 2/5] Add regression test --- .../narrowingByDiscriminantInLoop.js | 82 ++++++++++ .../narrowingByDiscriminantInLoop.symbols | 140 ++++++++++++++++ .../narrowingByDiscriminantInLoop.types | 153 ++++++++++++++++++ .../compiler/narrowingByDiscriminantInLoop.ts | 50 ++++++ 4 files changed, 425 insertions(+) create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.js create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.symbols create mode 100644 tests/baselines/reference/narrowingByDiscriminantInLoop.types create mode 100644 tests/cases/compiler/narrowingByDiscriminantInLoop.ts diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.js b/tests/baselines/reference/narrowingByDiscriminantInLoop.js new file mode 100644 index 0000000000000..b58357cff5b9c --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.js @@ -0,0 +1,82 @@ +//// [narrowingByDiscriminantInLoop.ts] +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; + +interface IDLTypeDescription { + origin: string; +} + +interface InterfaceType { + members: IDLMemberTypes[]; +} + +interface OperationMemberType { + type: "operation"; + idlType: IDLTypeDescription | null; +} + +interface ConstantMemberType { + type: "const"; + idlType: string; +} + +function insertInterface(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + (memberType.idlType as IDLTypeDescription); + } + } +} + +function insertInterface2(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} + +function foo(memberType: IDLMemberTypes) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} + +//// [narrowingByDiscriminantInLoop.js] +// Repro from #9977 +function insertInterface(callbackType) { + for (var _i = 0, _a = callbackType.members; _i < _a.length; _i++) { + var memberType = _a[_i]; + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + memberType.idlType; + } + } +} +function insertInterface2(callbackType) { + for (var _i = 0, _a = callbackType.members; _i < _a.length; _i++) { + var memberType = _a[_i]; + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} +function foo(memberType) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols new file mode 100644 index 0000000000000..30cd87f3c2b90 --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols @@ -0,0 +1,140 @@ +=== tests/cases/compiler/narrowingByDiscriminantInLoop.ts === +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) + +interface IDLTypeDescription { +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + + origin: string; +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +} + +interface InterfaceType { +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + members: IDLMemberTypes[]; +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) +} + +interface OperationMemberType { +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) + + type: "operation"; +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + idlType: IDLTypeDescription | null; +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +} + +interface ConstantMemberType { +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) + + type: "const"; +>type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + idlType: string; +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +} + +function insertInterface(callbackType: InterfaceType) { +>insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 20, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + for (const memberType of callbackType.members) { +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) + + if (memberType.type === "const") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType; // string +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) + } + else if (memberType.type === "operation") { +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + + (memberType.idlType as IDLTypeDescription); +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + } + } +} + +function insertInterface2(callbackType: InterfaceType) { +>insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 32, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) + + for (const memberType of callbackType.members) { +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) + + if (memberType.type === "operation") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + } + } +} + +function foo(memberType: IDLMemberTypes) { +>foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 40, 1)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) + + if (memberType.type === "const") { +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) + + memberType.idlType; // string +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) + } + else if (memberType.type === "operation") { +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) + + memberType.idlType.origin; // string +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.types b/tests/baselines/reference/narrowingByDiscriminantInLoop.types new file mode 100644 index 0000000000000..db8cb502a13b9 --- /dev/null +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.types @@ -0,0 +1,153 @@ +=== tests/cases/compiler/narrowingByDiscriminantInLoop.ts === +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; +>IDLMemberTypes : IDLMemberTypes +>OperationMemberType : OperationMemberType +>ConstantMemberType : ConstantMemberType + +interface IDLTypeDescription { +>IDLTypeDescription : IDLTypeDescription + + origin: string; +>origin : string +} + +interface InterfaceType { +>InterfaceType : InterfaceType + + members: IDLMemberTypes[]; +>members : IDLMemberTypes[] +>IDLMemberTypes : IDLMemberTypes +} + +interface OperationMemberType { +>OperationMemberType : OperationMemberType + + type: "operation"; +>type : "operation" + + idlType: IDLTypeDescription | null; +>idlType : IDLTypeDescription +>IDLTypeDescription : IDLTypeDescription +>null : null +} + +interface ConstantMemberType { +>ConstantMemberType : ConstantMemberType + + type: "const"; +>type : "const" + + idlType: string; +>idlType : string +} + +function insertInterface(callbackType: InterfaceType) { +>insertInterface : (callbackType: InterfaceType) => void +>callbackType : InterfaceType +>InterfaceType : InterfaceType + + for (const memberType of callbackType.members) { +>memberType : IDLMemberTypes +>callbackType.members : IDLMemberTypes[] +>callbackType : InterfaceType +>members : IDLMemberTypes[] + + if (memberType.type === "const") { +>memberType.type === "const" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"const" : "const" + + memberType.idlType; // string +>memberType.idlType : string +>memberType : ConstantMemberType +>idlType : string + } + else if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" +>memberType : OperationMemberType +>type : "operation" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + + (memberType.idlType as IDLTypeDescription); +>(memberType.idlType as IDLTypeDescription) : IDLTypeDescription +>memberType.idlType as IDLTypeDescription : IDLTypeDescription +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>IDLTypeDescription : IDLTypeDescription + } + } +} + +function insertInterface2(callbackType: InterfaceType) { +>insertInterface2 : (callbackType: InterfaceType) => void +>callbackType : InterfaceType +>InterfaceType : InterfaceType + + for (const memberType of callbackType.members) { +>memberType : IDLMemberTypes +>callbackType.members : IDLMemberTypes[] +>callbackType : InterfaceType +>members : IDLMemberTypes[] + + if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + } + } +} + +function foo(memberType: IDLMemberTypes) { +>foo : (memberType: IDLMemberTypes) => void +>memberType : IDLMemberTypes +>IDLMemberTypes : IDLMemberTypes + + if (memberType.type === "const") { +>memberType.type === "const" : boolean +>memberType.type : "operation" | "const" +>memberType : IDLMemberTypes +>type : "operation" | "const" +>"const" : "const" + + memberType.idlType; // string +>memberType.idlType : string +>memberType : ConstantMemberType +>idlType : string + } + else if (memberType.type === "operation") { +>memberType.type === "operation" : boolean +>memberType.type : "operation" +>memberType : OperationMemberType +>type : "operation" +>"operation" : "operation" + + memberType.idlType.origin; // string +>memberType.idlType.origin : string +>memberType.idlType : IDLTypeDescription +>memberType : OperationMemberType +>idlType : IDLTypeDescription +>origin : string + } +} diff --git a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts new file mode 100644 index 0000000000000..d9519c57a6229 --- /dev/null +++ b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts @@ -0,0 +1,50 @@ +// Repro from #9977 + +type IDLMemberTypes = OperationMemberType | ConstantMemberType; + +interface IDLTypeDescription { + origin: string; +} + +interface InterfaceType { + members: IDLMemberTypes[]; +} + +interface OperationMemberType { + type: "operation"; + idlType: IDLTypeDescription | null; +} + +interface ConstantMemberType { + type: "const"; + idlType: string; +} + +function insertInterface(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + (memberType.idlType as IDLTypeDescription); + } + } +} + +function insertInterface2(callbackType: InterfaceType) { + for (const memberType of callbackType.members) { + if (memberType.type === "operation") { + memberType.idlType.origin; // string + } + } +} + +function foo(memberType: IDLMemberTypes) { + if (memberType.type === "const") { + memberType.idlType; // string + } + else if (memberType.type === "operation") { + memberType.idlType.origin; // string + } +} \ No newline at end of file From ade89a60327763ac09bc7083d1f9ffc9935682b1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 06:41:05 -0700 Subject: [PATCH 3/5] Fix issue related to this and #8383 --- src/compiler/checker.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 691d74518959b..155f8a2e676e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7789,6 +7789,10 @@ namespace ts { return false; } + function rootContainsMatchingReference(source: Node, target: Node) { + return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (target).expression); + } + function isOrContainsMatchingReference(source: Node, target: Node) { return isMatchingReference(source, target) || containsMatchingReference(source, target); } @@ -8294,6 +8298,9 @@ namespace ts { if (isMatchingPropertyAccess(expr)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } + if (rootContainsMatchingReference(reference, expr)) { + return declaredType; + } return type; } @@ -8326,6 +8333,9 @@ namespace ts { if (isMatchingPropertyAccess(right)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } + if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) { + return declaredType; + } break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); From b3b4c34b609aeac08230251a9c68aa17d8eaf036 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 07:04:43 -0700 Subject: [PATCH 4/5] Add additional tests --- .../compiler/narrowingByDiscriminantInLoop.ts | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts index d9519c57a6229..f394018d2ce2c 100644 --- a/tests/cases/compiler/narrowingByDiscriminantInLoop.ts +++ b/tests/cases/compiler/narrowingByDiscriminantInLoop.ts @@ -1,3 +1,5 @@ +// @strictNullChecks: true + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -12,7 +14,7 @@ interface InterfaceType { interface OperationMemberType { type: "operation"; - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; } interface ConstantMemberType { @@ -47,4 +49,39 @@ function foo(memberType: IDLMemberTypes) { else if (memberType.type === "operation") { memberType.idlType.origin; // string } +} + +// Repro for issue similar to #8383 + +interface A { + kind: true; + prop: { a: string; }; +} + +interface B { + kind: false; + prop: { b: string; } +} + +function f1(x: A | B) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} + +function f2(x: A | B) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } } \ No newline at end of file From 0a90a4b27c83222748f1c2ca7776667c57ff044b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 1 Aug 2016 07:04:54 -0700 Subject: [PATCH 5/5] Accept new baselines --- .../narrowingByDiscriminantInLoop.js | 59 ++++- .../narrowingByDiscriminantInLoop.symbols | 238 ++++++++++++------ .../narrowingByDiscriminantInLoop.types | 112 ++++++++- 3 files changed, 336 insertions(+), 73 deletions(-) diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.js b/tests/baselines/reference/narrowingByDiscriminantInLoop.js index b58357cff5b9c..88cc87772dda4 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.js +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.js @@ -1,4 +1,5 @@ //// [narrowingByDiscriminantInLoop.ts] + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -13,7 +14,7 @@ interface InterfaceType { interface OperationMemberType { type: "operation"; - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; } interface ConstantMemberType { @@ -48,6 +49,41 @@ function foo(memberType: IDLMemberTypes) { else if (memberType.type === "operation") { memberType.idlType.origin; // string } +} + +// Repro for issue similar to #8383 + +interface A { + kind: true; + prop: { a: string; }; +} + +interface B { + kind: false; + prop: { b: string; } +} + +function f1(x: A | B) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} + +function f2(x: A | B) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } } //// [narrowingByDiscriminantInLoop.js] @@ -80,3 +116,24 @@ function foo(memberType) { memberType.idlType.origin; // string } } +function f1(x) { + while (true) { + x.prop; + if (x.kind === true) { + x.prop.a; + } + if (x.kind === false) { + x.prop.b; + } + } +} +function f2(x) { + while (true) { + if (x.kind) { + x.prop.a; + } + if (!x.kind) { + x.prop.b; + } + } +} diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols index 30cd87f3c2b90..edb0f1958c22d 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.symbols @@ -1,140 +1,238 @@ === tests/cases/compiler/narrowingByDiscriminantInLoop.ts === + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) ->OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) ->ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 11, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 16, 1)) interface IDLTypeDescription { ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) origin: string; ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) } interface InterfaceType { ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) members: IDLMemberTypes[]; ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) } interface OperationMemberType { ->OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 10, 1)) +>OperationMemberType : Symbol(OperationMemberType, Decl(narrowingByDiscriminantInLoop.ts, 11, 1)) type: "operation"; ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) - idlType: IDLTypeDescription | null; ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) + idlType: IDLTypeDescription; +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) } interface ConstantMemberType { ->ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 15, 1)) +>ConstantMemberType : Symbol(ConstantMemberType, Decl(narrowingByDiscriminantInLoop.ts, 16, 1)) type: "const"; ->type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>type : Symbol(ConstantMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) idlType: string; ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } function insertInterface(callbackType: InterfaceType) { ->insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 20, 1)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>insertInterface : Symbol(insertInterface, Decl(narrowingByDiscriminantInLoop.ts, 21, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 23, 25)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) for (const memberType of callbackType.members) { ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 22, 25)) ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 23, 25)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) if (memberType.type === "const") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType; // string ->memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } else if (memberType.type === "operation") { ->memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) (memberType.idlType as IDLTypeDescription); ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 23, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 2, 63)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 24, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>IDLTypeDescription : Symbol(IDLTypeDescription, Decl(narrowingByDiscriminantInLoop.ts, 3, 63)) } } } function insertInterface2(callbackType: InterfaceType) { ->insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 32, 1)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) ->InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 6, 1)) +>insertInterface2 : Symbol(insertInterface2, Decl(narrowingByDiscriminantInLoop.ts, 33, 1)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 35, 26)) +>InterfaceType : Symbol(InterfaceType, Decl(narrowingByDiscriminantInLoop.ts, 7, 1)) for (const memberType of callbackType.members) { ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) ->callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 34, 26)) ->members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 8, 25)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>callbackType.members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) +>callbackType : Symbol(callbackType, Decl(narrowingByDiscriminantInLoop.ts, 35, 26)) +>members : Symbol(InterfaceType.members, Decl(narrowingByDiscriminantInLoop.ts, 9, 25)) if (memberType.type === "operation") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 35, 14)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 36, 14)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) } } } function foo(memberType: IDLMemberTypes) { ->foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 40, 1)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) +>foo : Symbol(foo, Decl(narrowingByDiscriminantInLoop.ts, 41, 1)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) >IDLMemberTypes : Symbol(IDLMemberTypes, Decl(narrowingByDiscriminantInLoop.ts, 0, 0)) if (memberType.type === "const") { ->memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31), Decl(narrowingByDiscriminantInLoop.ts, 17, 30)) +>memberType.type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>type : Symbol(type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31), Decl(narrowingByDiscriminantInLoop.ts, 18, 30)) memberType.idlType; // string ->memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 18, 18)) +>memberType.idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>idlType : Symbol(ConstantMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 19, 18)) } else if (memberType.type === "operation") { ->memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 12, 31)) +>memberType.type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>type : Symbol(OperationMemberType.type, Decl(narrowingByDiscriminantInLoop.ts, 13, 31)) memberType.idlType.origin; // string ->memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) ->memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 42, 13)) ->idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 13, 22)) ->origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 4, 30)) +>memberType.idlType.origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) +>memberType.idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>memberType : Symbol(memberType, Decl(narrowingByDiscriminantInLoop.ts, 43, 13)) +>idlType : Symbol(OperationMemberType.idlType, Decl(narrowingByDiscriminantInLoop.ts, 14, 22)) +>origin : Symbol(IDLTypeDescription.origin, Decl(narrowingByDiscriminantInLoop.ts, 5, 30)) + } +} + +// Repro for issue similar to #8383 + +interface A { +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) + + kind: true; +>kind : Symbol(A.kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13)) + + prop: { a: string; }; +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +} + +interface B { +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + kind: false; +>kind : Symbol(B.kind, Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + prop: { b: string; } +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +} + +function f1(x: A | B) { +>f1 : Symbol(f1, Decl(narrowingByDiscriminantInLoop.ts, 62, 1)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + while (true) { + x.prop; +>x.prop : Symbol(prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15), Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15), Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) + + if (x.kind === true) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.a; +>x.prop.a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +>x.prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) + } + if (x.kind === false) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.b; +>x.prop.b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +>x.prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 64, 12)) +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) + } + } +} + +function f2(x: A | B) { +>f2 : Symbol(f2, Decl(narrowingByDiscriminantInLoop.ts, 74, 1)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>A : Symbol(A, Decl(narrowingByDiscriminantInLoop.ts, 50, 1)) +>B : Symbol(B, Decl(narrowingByDiscriminantInLoop.ts, 57, 1)) + + while (true) { + if (x.kind) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.a; +>x.prop.a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) +>x.prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>prop : Symbol(A.prop, Decl(narrowingByDiscriminantInLoop.ts, 55, 15)) +>a : Symbol(a, Decl(narrowingByDiscriminantInLoop.ts, 56, 11)) + } + if (!x.kind) { +>x.kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>kind : Symbol(kind, Decl(narrowingByDiscriminantInLoop.ts, 54, 13), Decl(narrowingByDiscriminantInLoop.ts, 59, 13)) + + x.prop.b; +>x.prop.b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) +>x.prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>x : Symbol(x, Decl(narrowingByDiscriminantInLoop.ts, 76, 12)) +>prop : Symbol(B.prop, Decl(narrowingByDiscriminantInLoop.ts, 60, 16)) +>b : Symbol(b, Decl(narrowingByDiscriminantInLoop.ts, 61, 11)) + } } } diff --git a/tests/baselines/reference/narrowingByDiscriminantInLoop.types b/tests/baselines/reference/narrowingByDiscriminantInLoop.types index db8cb502a13b9..50f322636cc2a 100644 --- a/tests/baselines/reference/narrowingByDiscriminantInLoop.types +++ b/tests/baselines/reference/narrowingByDiscriminantInLoop.types @@ -1,4 +1,5 @@ === tests/cases/compiler/narrowingByDiscriminantInLoop.ts === + // Repro from #9977 type IDLMemberTypes = OperationMemberType | ConstantMemberType; @@ -27,10 +28,9 @@ interface OperationMemberType { type: "operation"; >type : "operation" - idlType: IDLTypeDescription | null; + idlType: IDLTypeDescription; >idlType : IDLTypeDescription >IDLTypeDescription : IDLTypeDescription ->null : null } interface ConstantMemberType { @@ -151,3 +151,111 @@ function foo(memberType: IDLMemberTypes) { >origin : string } } + +// Repro for issue similar to #8383 + +interface A { +>A : A + + kind: true; +>kind : true +>true : true + + prop: { a: string; }; +>prop : { a: string; } +>a : string +} + +interface B { +>B : B + + kind: false; +>kind : false +>false : false + + prop: { b: string; } +>prop : { b: string; } +>b : string +} + +function f1(x: A | B) { +>f1 : (x: A | B) => void +>x : A | B +>A : A +>B : B + + while (true) { +>true : boolean + + x.prop; +>x.prop : { a: string; } | { b: string; } +>x : A | B +>prop : { a: string; } | { b: string; } + + if (x.kind === true) { +>x.kind === true : boolean +>x.kind : boolean +>x : A | B +>kind : boolean +>true : true + + x.prop.a; +>x.prop.a : string +>x.prop : { a: string; } +>x : A +>prop : { a: string; } +>a : string + } + if (x.kind === false) { +>x.kind === false : boolean +>x.kind : boolean +>x : A | B +>kind : boolean +>false : false + + x.prop.b; +>x.prop.b : string +>x.prop : { b: string; } +>x : B +>prop : { b: string; } +>b : string + } + } +} + +function f2(x: A | B) { +>f2 : (x: A | B) => void +>x : A | B +>A : A +>B : B + + while (true) { +>true : boolean + + if (x.kind) { +>x.kind : boolean +>x : A | B +>kind : boolean + + x.prop.a; +>x.prop.a : string +>x.prop : { a: string; } +>x : A +>prop : { a: string; } +>a : string + } + if (!x.kind) { +>!x.kind : boolean +>x.kind : boolean +>x : A | B +>kind : boolean + + x.prop.b; +>x.prop.b : string +>x.prop : { b: string; } +>x : B +>prop : { b: string; } +>b : string + } + } +}