From e944bfab52e9863cc56a36848473d5f6f0452ab8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 10 May 2016 09:35:23 -0700 Subject: [PATCH] Revert to declared type when narrowing produces the nothing type --- src/compiler/binder.ts | 19 +------------------ src/compiler/checker.ts | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index fb24f4cc45e..05dbd47006b 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -696,23 +696,6 @@ namespace ts { }; } - function skipSimpleConditionalFlow(flow: FlowNode) { - // We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain - // no constructs that affect control flow type analysis. Such simple flows have no effect on the - // code paths that follow and ignoring them means we'll do less work. - if (flow.flags & FlowFlags.BranchLabel && (flow).antecedents.length === 2) { - const a = (flow).antecedents[0]; - const b = (flow).antecedents[1]; - if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition || - a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) && - (a).antecedent === (b).antecedent && - (a).expression === (b).expression) { - return (a).antecedent; - } - } - return flow; - } - function finishFlowLabel(flow: FlowLabel): FlowNode { const antecedents = flow.antecedents; if (!antecedents) { @@ -721,7 +704,7 @@ namespace ts { if (antecedents.length === 1) { return antecedents[0]; } - return skipSimpleConditionalFlow(flow); + return flow; } function isStatementCondition(node: Node) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index eea4d3f0a2d..cd0758fe5c3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7702,7 +7702,22 @@ namespace ts { } function getTypeAtFlowCondition(flow: FlowCondition) { - return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, (flow.flags & FlowFlags.TrueCondition) !== 0); + let type = getTypeAtFlowNode(flow.antecedent); + if (type !== emptyUnionType) { + // If we have an antecedent type (meaning we're reachable in some way), we first + // attempt to narrow the antecedent type. If that produces the nothing type, then + // we take the type guard as an indication that control could reach here in a + // manner not understood by the control flow analyzer (e.g. a function argument + // has an invalid type, or a nested function has possibly made an assignment to a + // captured variable). We proceed by reverting to the declared type and then + // narrow that. + const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; + type = narrowType(type, flow.expression, assumeTrue); + if (type === emptyUnionType) { + type = narrowType(declaredType, flow.expression, assumeTrue); + } + } + return type; } function getTypeAtFlowBranchLabel(flow: FlowLabel) {