From e67d15a1ce0503672c4d0e37bd3faadbb78ee98c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Mar 2016 10:20:43 -0700 Subject: [PATCH 01/67] Initial implementation of control flow based type analysis --- src/compiler/binder.ts | 706 ++++++++++++++++++++++++---------------- src/compiler/checker.ts | 482 +++++++++++---------------- src/compiler/types.ts | 34 ++ 3 files changed, 650 insertions(+), 572 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6957be484f35a..b49e22e8192f5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -11,19 +11,11 @@ namespace ts { ConstEnumOnly = 2 } - const enum Reachability { - Uninitialized = 1 << 0, - Reachable = 1 << 1, - Unreachable = 1 << 2, - ReportedUnreachable = 1 << 3 - } - - function or(state1: Reachability, state2: Reachability): Reachability { - return (state1 | state2) & Reachability.Reachable - ? Reachability.Reachable - : (state1 & state2) & Reachability.ReportedUnreachable - ? Reachability.ReportedUnreachable - : Reachability.Unreachable; + interface ActiveLabel { + name: string; + breakTarget: FlowLabel; + continueTarget: FlowLabel; + referenced: boolean; } export function getModuleInstanceState(node: Node): ModuleInstanceState { @@ -112,10 +104,11 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; - let currentReachabilityState: Reachability; - let labelStack: Reachability[]; - let labelIndexMap: Map; - let implicitLabels: number[]; + let currentFlow: FlowNode; + let breakTarget: FlowLabel; + let continueTarget: FlowLabel; + let preSwitchCaseFlow: FlowNode; + let activeLabels: ActiveLabel[]; // state used for emit helpers let hasClassExtends: boolean; @@ -132,6 +125,9 @@ namespace ts { let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; + const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + const reportedUncreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; options = opts; @@ -154,9 +150,10 @@ namespace ts { lastContainer = undefined; seenThisKeyword = false; hasExplicitReturn = false; - labelStack = undefined; - labelIndexMap = undefined; - implicitLabels = undefined; + currentFlow = undefined; + breakTarget = undefined; + continueTarget = undefined; + activeLabels = undefined; hasClassExtends = false; hasAsyncFunctions = false; hasDecorators = false; @@ -436,11 +433,11 @@ namespace ts { blockScopeContainer.locals = undefined; } - let savedReachabilityState: Reachability; - let savedLabelStack: Reachability[]; - let savedLabels: Map; - let savedImplicitLabels: number[]; let savedHasExplicitReturn: boolean; + let savedCurrentFlow: FlowNode; + let savedBreakTarget: FlowLabel; + let savedContinueTarget: FlowLabel; + let savedActiveLabels: ActiveLabel[]; const kind = node.kind; let flags = node.flags; @@ -457,15 +454,17 @@ namespace ts { const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind); if (saveState) { - savedReachabilityState = currentReachabilityState; - savedLabelStack = labelStack; - savedLabels = labelIndexMap; - savedImplicitLabels = implicitLabels; savedHasExplicitReturn = hasExplicitReturn; + savedCurrentFlow = currentFlow; + savedBreakTarget = breakTarget; + savedContinueTarget = continueTarget; + savedActiveLabels = activeLabels; - currentReachabilityState = Reachability.Reachable; hasExplicitReturn = false; - labelStack = labelIndexMap = implicitLabels = undefined; + currentFlow = { kind: FlowKind.Start }; + breakTarget = undefined; + continueTarget = undefined; + activeLabels = undefined; } if (isInJavaScriptFile(node) && node.jsDocComment) { @@ -474,7 +473,7 @@ namespace ts { bindReachableStatement(node); - if (currentReachabilityState === Reachability.Reachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { + if (currentFlow.kind !== FlowKind.Unreachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) { flags |= NodeFlags.HasExplicitReturn; @@ -503,11 +502,11 @@ namespace ts { node.flags = flags; if (saveState) { + activeLabels = savedActiveLabels; + continueTarget = savedContinueTarget; + breakTarget = savedBreakTarget; + currentFlow = savedCurrentFlow; hasExplicitReturn = savedHasExplicitReturn; - currentReachabilityState = savedReachabilityState; - labelStack = savedLabelStack; - labelIndexMap = savedLabels; - implicitLabels = savedImplicitLabels; } container = saveContainer; @@ -562,174 +561,381 @@ namespace ts { case SyntaxKind.LabeledStatement: bindLabeledStatement(node); break; + case SyntaxKind.BinaryExpression: + bindBinaryExpressionFlow(node); + break; + case SyntaxKind.ConditionalExpression: + bindConditionalExpressionFlow(node); + break; + case SyntaxKind.VariableDeclaration: + bindVariableDeclarationFlow(node); + break; default: forEachChild(node, bind); break; } } - function bindWhileStatement(n: WhileStatement): void { - const preWhileState = - n.expression.kind === SyntaxKind.FalseKeyword ? Reachability.Unreachable : currentReachabilityState; - const postWhileState = - n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : currentReachabilityState; - - // bind expressions (don't affect reachability) - bind(n.expression); - - currentReachabilityState = preWhileState; - const postWhileLabel = pushImplicitLabel(); - bind(n.statement); - popImplicitLabel(postWhileLabel, postWhileState); + function isNarrowableReference(expr: Expression): boolean { + return expr.kind === SyntaxKind.Identifier || + expr.kind === SyntaxKind.ThisKeyword || + expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); } - function bindDoStatement(n: DoStatement): void { - const preDoState = currentReachabilityState; - - const postDoLabel = pushImplicitLabel(); - bind(n.statement); - const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : preDoState; - popImplicitLabel(postDoLabel, postDoState); - - // bind expressions (don't affect reachability) - bind(n.expression); + function isNarrowingExpression(expr: Expression): boolean { + switch (expr.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.ThisKeyword: + case SyntaxKind.PropertyAccessExpression: + return isNarrowableReference(expr); + case SyntaxKind.CallExpression: + return true; + case SyntaxKind.ParenthesizedExpression: + return isNarrowingExpression((expr).expression); + case SyntaxKind.BinaryExpression: + return isNarrowingBinaryExpression(expr); + case SyntaxKind.PrefixUnaryExpression: + return (expr).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr).operand); + } + return false; } - function bindForStatement(n: ForStatement): void { - const preForState = currentReachabilityState; - const postForLabel = pushImplicitLabel(); - - // bind expressions (don't affect reachability) - bind(n.initializer); - bind(n.condition); - bind(n.incrementor); - - bind(n.statement); - - // for statement is considered infinite when it condition is either omitted or is true keyword - // - for(..;;..) - // - for(..;true;..) - const isInfiniteLoop = (!n.condition || n.condition.kind === SyntaxKind.TrueKeyword); - const postForState = isInfiniteLoop ? Reachability.Unreachable : preForState; - popImplicitLabel(postForLabel, postForState); + function isNarrowingBinaryExpression(expr: BinaryExpression) { + switch (expr.operatorToken.kind) { + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + if (isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) { + return true; + } + if (expr.left.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((expr.left).expression) && expr.right.kind === SyntaxKind.StringLiteral) { + return true; + } + return false; + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.BarBarToken: + return isNarrowingExpression(expr.left) || isNarrowingExpression(expr.right); + case SyntaxKind.InstanceOfKeyword: + return isNarrowingExpression(expr.left); + } + return false; } - function bindForInOrForOfStatement(n: ForInStatement | ForOfStatement): void { - const preStatementState = currentReachabilityState; - const postStatementLabel = pushImplicitLabel(); - - // bind expressions (don't affect reachability) - bind(n.initializer); - bind(n.expression); - - bind(n.statement); - popImplicitLabel(postStatementLabel, preStatementState); + function createFlowLabel(): FlowLabel { + return { + kind: FlowKind.Label, + antecedents: undefined + }; } - function bindIfStatement(n: IfStatement): void { - // denotes reachability state when entering 'thenStatement' part of the if statement: - // i.e. if condition is false then thenStatement is unreachable - const ifTrueState = n.expression.kind === SyntaxKind.FalseKeyword ? Reachability.Unreachable : currentReachabilityState; - // denotes reachability state when entering 'elseStatement': - // i.e. if condition is true then elseStatement is unreachable - const ifFalseState = n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : currentReachabilityState; - - currentReachabilityState = ifTrueState; - - // bind expression (don't affect reachability) - bind(n.expression); + function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { + if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) { + (label.antecedents || (label.antecedents = [])).push(antecedent); + } + } - bind(n.thenStatement); - if (n.elseStatement) { - const preElseState = currentReachabilityState; - currentReachabilityState = ifFalseState; - bind(n.elseStatement); - currentReachabilityState = or(currentReachabilityState, preElseState); + function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode { + if (!expression) { + return assumeTrue ? antecedent : unreachableFlow; } - else { - currentReachabilityState = or(currentReachabilityState, ifFalseState); + if (expression.kind === SyntaxKind.TrueKeyword && !assumeTrue || expression.kind === SyntaxKind.FalseKeyword && assumeTrue) { + return unreachableFlow; + } + if (!isNarrowingExpression(expression)) { + return antecedent; } + return { + kind: FlowKind.Condition, + antecedent, + expression, + assumeTrue + }; } - function bindReturnOrThrow(n: ReturnStatement | ThrowStatement): void { - // bind expression (don't affect reachability) - bind(n.expression); - if (n.kind === SyntaxKind.ReturnStatement) { + function createFlowAssignment(antecedent: FlowNode, node: BinaryExpression | VariableDeclaration | ForInStatement | ForOfStatement): FlowNode { + return { + kind: FlowKind.Assignment, + antecedent, + node + }; + } + + function finishFlow(flow: FlowNode): FlowNode { + while (flow.kind === FlowKind.Label) { + const antecedents = (flow).antecedents; + if (!antecedents) { + return unreachableFlow; + } + if (antecedents.length > 1) { + break; + } + flow = antecedents[0]; + } + return flow; + } + + function bindWhileStatement(node: WhileStatement): void { + const preWhileLabel = createFlowLabel(); + const postWhileLabel = createFlowLabel(); + addAntecedent(preWhileLabel, currentFlow); + currentFlow = preWhileLabel; + bind(node.expression); + addAntecedent(postWhileLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ false)); + currentFlow = createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true); + const saveBreakTarget = breakTarget; + const saveContinueTarget = continueTarget; + breakTarget = postWhileLabel; + continueTarget = preWhileLabel; + bind(node.statement); + breakTarget = saveBreakTarget; + continueTarget = saveContinueTarget; + addAntecedent(preWhileLabel, currentFlow); + currentFlow = finishFlow(postWhileLabel); + } + + function bindDoStatement(node: DoStatement): void { + const preDoLabel = createFlowLabel(); + const postDoLabel = createFlowLabel(); + addAntecedent(preDoLabel, currentFlow); + currentFlow = preDoLabel; + const saveBreakTarget = breakTarget; + const saveContinueTarget = continueTarget; + breakTarget = postDoLabel; + continueTarget = preDoLabel; + bind(node.statement); + breakTarget = saveBreakTarget; + continueTarget = saveContinueTarget; + bind(node.expression); + addAntecedent(preDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true)); + addAntecedent(postDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ false)); + currentFlow = finishFlow(postDoLabel); + } + + function bindForStatement(node: ForStatement): void { + const preLoopLabel = createFlowLabel(); + const postLoopLabel = createFlowLabel(); + bind(node.initializer); + addAntecedent(preLoopLabel, currentFlow); + currentFlow = preLoopLabel; + bind(node.condition); + addAntecedent(postLoopLabel, createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ false)); + currentFlow = createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ true); + const saveBreakTarget = breakTarget; + const saveContinueTarget = continueTarget; + breakTarget = postLoopLabel; + continueTarget = preLoopLabel; + bind(node.statement); + bind(node.incrementor); + breakTarget = saveBreakTarget; + continueTarget = saveContinueTarget; + addAntecedent(preLoopLabel, currentFlow); + currentFlow = finishFlow(postLoopLabel); + } + + function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { + const preLoopLabel = createFlowLabel(); + const postLoopLabel = createFlowLabel(); + bind(node.initializer); + bind(node.expression); + addAntecedent(preLoopLabel, currentFlow); + addAntecedent(postLoopLabel, currentFlow); + currentFlow = preLoopLabel; + const saveBreakTarget = breakTarget; + const saveContinueTarget = continueTarget; + breakTarget = postLoopLabel; + continueTarget = preLoopLabel; + currentFlow = createFlowAssignment(currentFlow, node); + bind(node.statement); + breakTarget = saveBreakTarget; + continueTarget = saveContinueTarget; + addAntecedent(preLoopLabel, currentFlow); + addAntecedent(postLoopLabel, currentFlow); + currentFlow = finishFlow(postLoopLabel); + } + + function bindIfStatement(node: IfStatement): void { + const postIfLabel = createFlowLabel(); + bind(node.expression); + const postConditionFlow = currentFlow; + currentFlow = createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true); + bind(node.thenStatement); + addAntecedent(postIfLabel, currentFlow); + currentFlow = createFlowCondition(postConditionFlow, node.expression, /*assumeTrue*/ false); + bind(node.elseStatement); + addAntecedent(postIfLabel, currentFlow); + currentFlow = finishFlow(postIfLabel); + } + + function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void { + bind(node.expression); + if (node.kind === SyntaxKind.ReturnStatement) { hasExplicitReturn = true; } - currentReachabilityState = Reachability.Unreachable; + currentFlow = unreachableFlow; } - function bindBreakOrContinueStatement(n: BreakOrContinueStatement): void { - // call bind on label (don't affect reachability) - bind(n.label); - // for continue case touch label so it will be marked a used - const isValidJump = jumpToLabel(n.label, n.kind === SyntaxKind.BreakStatement ? currentReachabilityState : Reachability.Unreachable); - if (isValidJump) { - currentReachabilityState = Reachability.Unreachable; + function findActiveLabel(name: string) { + if (activeLabels) { + for (const label of activeLabels) { + if (label.name === name) { + return label; + } + } } + return undefined; } - function bindTryStatement(n: TryStatement): void { - // catch\finally blocks has the same reachability as try block - const preTryState = currentReachabilityState; - bind(n.tryBlock); - const postTryState = currentReachabilityState; - - currentReachabilityState = preTryState; - bind(n.catchClause); - const postCatchState = currentReachabilityState; - - currentReachabilityState = preTryState; - bind(n.finallyBlock); - - // post catch/finally state is reachable if - // - post try state is reachable - control flow can fall out of try block - // - post catch state is reachable - control flow can fall out of catch block - currentReachabilityState = n.catchClause ? or(postTryState, postCatchState) : postTryState; + function bindbreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel, continueTarget: FlowLabel) { + const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget; + if (flowLabel) { + addAntecedent(flowLabel, currentFlow); + currentFlow = unreachableFlow; + } } - function bindSwitchStatement(n: SwitchStatement): void { - const preSwitchState = currentReachabilityState; - const postSwitchLabel = pushImplicitLabel(); - - // bind expression (don't affect reachability) - bind(n.expression); - - bind(n.caseBlock); - - const hasDefault = forEach(n.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); + function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void { + bind(node.label); + if (node.label) { + const activeLabel = findActiveLabel(node.label.text); + if (activeLabel) { + activeLabel.referenced = true; + bindbreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); + } + } + else { + bindbreakOrContinueFlow(node, breakTarget, continueTarget); + } + } + + function bindTryStatement(node: TryStatement): void { + const postFinallyLabel = createFlowLabel(); + const preTryFlow = currentFlow; + // TODO: Every statement in try block is potentially an exit point! + bind(node.tryBlock); + addAntecedent(postFinallyLabel, currentFlow); + if (node.catchClause) { + currentFlow = preTryFlow; + bind(node.catchClause); + addAntecedent(postFinallyLabel, currentFlow); + } + if (node.finallyBlock) { + currentFlow = preTryFlow; + bind(node.finallyBlock); + } + currentFlow = finishFlow(postFinallyLabel); + } + + function bindSwitchStatement(node: SwitchStatement): void { + const postSwitchLabel = createFlowLabel(); + bind(node.expression); + const saveBreakTarget = breakTarget; + const savePreSwitchCaseFlow = preSwitchCaseFlow; + breakTarget = postSwitchLabel; + preSwitchCaseFlow = currentFlow; + bind(node.caseBlock); + addAntecedent(postSwitchLabel, currentFlow); + const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); + if (!hasDefault) { + addAntecedent(postSwitchLabel, preSwitchCaseFlow); + } + breakTarget = saveBreakTarget; + preSwitchCaseFlow = savePreSwitchCaseFlow; + currentFlow = finishFlow(postSwitchLabel); + } + + function bindCaseBlock(node: CaseBlock): void { + const clauses = node.clauses; + for (let i = 0; i < clauses.length; i++) { + const clause = clauses[i]; + if (clause.statements.length) { + if (currentFlow.kind === FlowKind.Unreachable) { + currentFlow = preSwitchCaseFlow; + } + else { + const preCaseLabel = createFlowLabel(); + addAntecedent(preCaseLabel, preSwitchCaseFlow); + addAntecedent(preCaseLabel, currentFlow); + currentFlow = finishFlow(preCaseLabel); + } + bind(clause); + if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { + errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); + } + } + else { + bind(clause); + } + } + } - // post switch state is unreachable if switch is exhaustive (has a default case ) and does not have fallthrough from the last case - const postSwitchState = hasDefault && currentReachabilityState !== Reachability.Reachable ? Reachability.Unreachable : preSwitchState; + function pushActiveLabel(name: string, breakTarget: FlowLabel, continueTarget: FlowLabel): ActiveLabel { + const activeLabel = { + name, + breakTarget, + continueTarget, + referenced: false + }; + (activeLabels || (activeLabels = [])).push(activeLabel); + return activeLabel; + } - popImplicitLabel(postSwitchLabel, postSwitchState); + function popActiveLabel() { + activeLabels.pop(); } - function bindCaseBlock(n: CaseBlock): void { - const startState = currentReachabilityState; + function bindLabeledStatement(node: LabeledStatement): void { + const preStatementLabel = createFlowLabel(); + const postStatementLabel = createFlowLabel(); + bind(node.label); + addAntecedent(preStatementLabel, currentFlow); + const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel); + bind(node.statement); + popActiveLabel(); + if (!activeLabel.referenced && !options.allowUnusedLabels) { + file.bindDiagnostics.push(createDiagnosticForNode(node.label, Diagnostics.Unused_label)); + } + addAntecedent(postStatementLabel, currentFlow); + currentFlow = finishFlow(postStatementLabel); + } - for (let i = 0; i < n.clauses.length; i++) { - const clause = n.clauses[i]; - currentReachabilityState = startState; - bind(clause); - if (clause.statements.length && - i !== n.clauses.length - 1 && // allow fallthrough from the last case - currentReachabilityState === Reachability.Reachable && - options.noFallthroughCasesInSwitch) { - errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); + function bindBinaryExpressionFlow(node: BinaryExpression) { + const operator = node.operatorToken.kind; + if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { + const postExpressionLabel = createFlowLabel(); + bind(node.left); + bind(node.operatorToken); + addAntecedent(postExpressionLabel, currentFlow); + currentFlow = createFlowCondition(currentFlow, node.left, /*assumeTrue*/ operator === SyntaxKind.AmpersandAmpersandToken); + bind(node.right); + addAntecedent(postExpressionLabel, currentFlow); + currentFlow = finishFlow(postExpressionLabel); + } + else { + forEachChild(node, bind); + if (operator === SyntaxKind.EqualsToken) { + currentFlow = createFlowAssignment(currentFlow, node); } } } - function bindLabeledStatement(n: LabeledStatement): void { - // call bind on label (don't affect reachability) - bind(n.label); + function bindConditionalExpressionFlow(node: ConditionalExpression) { + const postExpressionLabel = createFlowLabel(); + bind(node.condition); + const postConditionFlow = currentFlow; + currentFlow = createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ true); + bind(node.whenTrue); + addAntecedent(postExpressionLabel, currentFlow); + currentFlow = createFlowCondition(postConditionFlow, node.condition, /*assumeTrue*/ false); + bind(node.whenFalse); + addAntecedent(postExpressionLabel, currentFlow); + currentFlow = finishFlow(postExpressionLabel); + } - const ok = pushNamedLabel(n.label); - bind(n.statement); - if (ok) { - popNamedLabel(n.label, currentReachabilityState); + function bindVariableDeclarationFlow(node: VariableDeclaration) { + forEachChild(node, bind); + if (node.initializer) { + currentFlow = createFlowAssignment(currentFlow, node); } } @@ -1239,7 +1445,16 @@ namespace ts { switch (node.kind) { /* Strict mode checks */ case SyntaxKind.Identifier: + case SyntaxKind.ThisKeyword: + if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { + node.flowNode = currentFlow; + } return checkStrictModeIdentifier(node); + case SyntaxKind.PropertyAccessExpression: + if (currentFlow && isNarrowableReference(node)) { + node.flowNode = currentFlow; + } + break; case SyntaxKind.BinaryExpression: if (isInJavaScriptFile(node)) { const specialKind = getSpecialPropertyAssignmentKind(node); @@ -1663,132 +1878,53 @@ namespace ts { // reachability checks - function pushNamedLabel(name: Identifier): boolean { - initializeReachabilityStateIfNecessary(); - - if (hasProperty(labelIndexMap, name.text)) { - return false; - } - labelIndexMap[name.text] = labelStack.push(Reachability.Uninitialized) - 1; - return true; - } - - function pushImplicitLabel(): number { - initializeReachabilityStateIfNecessary(); - - const index = labelStack.push(Reachability.Uninitialized) - 1; - implicitLabels.push(index); - return index; + function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean { + const instanceState = getModuleInstanceState(node); + return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && options.preserveConstEnums); } - function popNamedLabel(label: Identifier, outerState: Reachability): void { - const index = labelIndexMap[label.text]; - Debug.assert(index !== undefined); - Debug.assert(labelStack.length == index + 1); - - labelIndexMap[label.text] = undefined; - - setCurrentStateAtLabel(labelStack.pop(), outerState, label); - } - - function popImplicitLabel(implicitLabelIndex: number, outerState: Reachability): void { - if (labelStack.length !== implicitLabelIndex + 1) { - Debug.assert(false, `Label stack: ${labelStack.length}, index:${implicitLabelIndex}`); - } - - const i = implicitLabels.pop(); - - if (implicitLabelIndex !== i) { - Debug.assert(false, `i: ${i}, index: ${implicitLabelIndex}`); - } - - setCurrentStateAtLabel(labelStack.pop(), outerState, /*name*/ undefined); - } - - function setCurrentStateAtLabel(innerMergedState: Reachability, outerState: Reachability, label: Identifier): void { - if (innerMergedState === Reachability.Uninitialized) { - if (label && !options.allowUnusedLabels) { - file.bindDiagnostics.push(createDiagnosticForNode(label, Diagnostics.Unused_label)); - } - currentReachabilityState = outerState; - } - else { - currentReachabilityState = or(innerMergedState, outerState); - } - } - - function jumpToLabel(label: Identifier, outerState: Reachability): boolean { - initializeReachabilityStateIfNecessary(); - - const index = label ? labelIndexMap[label.text] : lastOrUndefined(implicitLabels); - if (index === undefined) { - // reference to unknown label or - // break/continue used outside of loops + function checkUnreachable(node: Node): boolean { + if (currentFlow.kind !== FlowKind.Unreachable) { return false; } - const stateAtLabel = labelStack[index]; - labelStack[index] = stateAtLabel === Reachability.Uninitialized ? outerState : or(stateAtLabel, outerState); - return true; - } - - function checkUnreachable(node: Node): boolean { - switch (currentReachabilityState) { - case Reachability.Unreachable: - const reportError = - // report error on all statements except empty ones - (isStatement(node) && node.kind !== SyntaxKind.EmptyStatement) || - // report error on class declarations - node.kind === SyntaxKind.ClassDeclaration || - // report error on instantiated modules or const-enums only modules if preserveConstEnums is set - (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node)) || - // report error on regular enums and const enums if preserveConstEnums is set - (node.kind === SyntaxKind.EnumDeclaration && (!isConstEnumDeclaration(node) || options.preserveConstEnums)); - - if (reportError) { - currentReachabilityState = Reachability.ReportedUnreachable; - - // unreachable code is reported if - // - user has explicitly asked about it AND - // - statement is in not ambient context (statements in ambient context is already an error - // so we should not report extras) AND - // - node is not variable statement OR - // - node is block scoped variable statement OR - // - node is not block scoped variable statement and at least one variable declaration has initializer - // Rationale: we don't want to report errors on non-initialized var's since they are hoisted - // On the other side we do want to report errors on non-initialized 'lets' because of TDZ - const reportUnreachableCode = - !options.allowUnreachableCode && - !isInAmbientContext(node) && - ( - node.kind !== SyntaxKind.VariableStatement || - getCombinedNodeFlags((node).declarationList) & NodeFlags.BlockScoped || - forEach((node).declarationList.declarations, d => d.initializer) - ); - - if (reportUnreachableCode) { - errorOnFirstToken(node, Diagnostics.Unreachable_code_detected); - } + if (currentFlow === unreachableFlow) { + const reportError = + // report error on all statements except empty ones + (isStatement(node) && node.kind !== SyntaxKind.EmptyStatement) || + // report error on class declarations + node.kind === SyntaxKind.ClassDeclaration || + // report error on instantiated modules or const-enums only modules if preserveConstEnums is set + (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node)) || + // report error on regular enums and const enums if preserveConstEnums is set + (node.kind === SyntaxKind.EnumDeclaration && (!isConstEnumDeclaration(node) || options.preserveConstEnums)); + + if (reportError) { + currentFlow = reportedUncreachableFlow; + + // unreachable code is reported if + // - user has explicitly asked about it AND + // - statement is in not ambient context (statements in ambient context is already an error + // so we should not report extras) AND + // - node is not variable statement OR + // - node is block scoped variable statement OR + // - node is not block scoped variable statement and at least one variable declaration has initializer + // Rationale: we don't want to report errors on non-initialized var's since they are hoisted + // On the other side we do want to report errors on non-initialized 'lets' because of TDZ + const reportUnreachableCode = + !options.allowUnreachableCode && + !isInAmbientContext(node) && + ( + node.kind !== SyntaxKind.VariableStatement || + getCombinedNodeFlags((node).declarationList) & NodeFlags.BlockScoped || + forEach((node).declarationList.declarations, d => d.initializer) + ); + + if (reportUnreachableCode) { + errorOnFirstToken(node, Diagnostics.Unreachable_code_detected); } - case Reachability.ReportedUnreachable: - return true; - default: - return false; - } - - function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean { - const instanceState = getModuleInstanceState(node); - return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && options.preserveConstEnums); - } - } - - function initializeReachabilityStateIfNecessary(): void { - if (labelIndexMap) { - return; + } } - currentReachabilityState = Reachability.Reachable; - labelIndexMap = {}; - labelStack = []; - implicitLabels = []; + return true; } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92c5065b7dd8f..da5cc90caa41c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5,6 +5,7 @@ namespace ts { let nextSymbolId = 1; let nextNodeId = 1; let nextMergeId = 1; + let nextFlowId = 1; export function getNodeId(node: Node): number { if (!node.id) { @@ -118,6 +119,7 @@ namespace ts { const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null"); const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined"); const unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); + const resolvingFlowType = createIntrinsicType(TypeFlags.Void, "__resolving__"); const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyUnionType = emptyObjectType; @@ -186,6 +188,7 @@ namespace ts { const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; + const flowTypeCaches: Map[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -7136,11 +7139,11 @@ namespace ts { Debug.fail("should not get here"); } - // Return the assignment key for a "dotted name" (i.e. a sequence of identifiers + // Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers // separated by dots). The key consists of the id of the symbol referenced by the // leftmost identifier followed by zero or more property names separated by dots. // The result is undefined if the reference isn't a dotted name. - function getAssignmentKey(node: Node): string { + function getFlowCacheKey(node: Node): string { if (node.kind === SyntaxKind.Identifier) { const symbol = getResolvedSymbol(node); return symbol !== unknownSymbol ? "" + getSymbolId(symbol) : undefined; @@ -7149,125 +7152,12 @@ namespace ts { return "0"; } if (node.kind === SyntaxKind.PropertyAccessExpression) { - const key = getAssignmentKey((node).expression); + const key = getFlowCacheKey((node).expression); return key && key + "." + (node).name.text; } return undefined; } - function hasInitializer(node: VariableLikeDeclaration): boolean { - return !!(node.initializer || isBindingPattern(node.parent) && hasInitializer(node.parent.parent)); - } - - // For a given node compute a map of which dotted names are assigned within - // the node. - function getAssignmentMap(node: Node): Map { - const assignmentMap: Map = {}; - visit(node); - return assignmentMap; - - function visitReference(node: Identifier | PropertyAccessExpression) { - if (isAssignmentTarget(node) || isCompoundAssignmentTarget(node)) { - const key = getAssignmentKey(node); - if (key) { - assignmentMap[key] = true; - } - } - forEachChild(node, visit); - } - - function visitVariableDeclaration(node: VariableLikeDeclaration) { - if (!isBindingPattern(node.name) && hasInitializer(node)) { - assignmentMap[getSymbolId(getSymbolOfNode(node))] = true; - } - forEachChild(node, visit); - } - - function visit(node: Node) { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PropertyAccessExpression: - visitReference(node); - break; - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - visitVariableDeclaration(node); - break; - case SyntaxKind.BinaryExpression: - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.DeleteExpression: - case SyntaxKind.AwaitExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.VoidExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.YieldExpression: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.Block: - case SyntaxKind.VariableStatement: - case SyntaxKind.ExpressionStatement: - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseBlock: - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - case SyntaxKind.LabeledStatement: - case SyntaxKind.ThrowStatement: - case SyntaxKind.TryStatement: - case SyntaxKind.CatchClause: - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxExpression: - forEachChild(node, visit); - break; - } - } - } - - function isReferenceAssignedWithin(reference: Node, node: Node): boolean { - if (reference.kind !== SyntaxKind.ThisKeyword) { - const key = getAssignmentKey(reference); - if (key) { - const links = getNodeLinks(node); - return (links.assignmentMap || (links.assignmentMap = getAssignmentMap(node)))[key]; - } - } - return false; - } - - function isAnyPartOfReferenceAssignedWithin(reference: Node, node: Node) { - while (true) { - if (isReferenceAssignedWithin(reference, node)) { - return true; - } - if (reference.kind !== SyntaxKind.PropertyAccessExpression) { - return false; - } - reference = (reference).expression; - } - } - function isNullOrUndefinedLiteral(node: Expression) { return node.kind === SyntaxKind.NullKeyword || node.kind === SyntaxKind.Identifier && getResolvedSymbol(node) === undefinedSymbol; @@ -7299,16 +7189,68 @@ namespace ts { return false; } - // Get the narrowed type of a given symbol at a given location + function containsMatchingReference(source: Node, target: Node) { + while (true) { + if (isMatchingReference(source, target)) { + return true; + } + if (source.kind !== SyntaxKind.PropertyAccessExpression) { + return false; + } + source = (source).expression; + } + } + + function hasMatchingArgument(callExpression: CallExpression, target: Node) { + if (callExpression.arguments) { + for (const argument of callExpression.arguments) { + if (isMatchingReference(argument, target)) { + return true; + } + } + } + if (callExpression.expression.kind === SyntaxKind.PropertyAccessExpression && + isMatchingReference((callExpression.expression).expression, target)) { + return true; + } + return false; + } + + function getFlowTypeCache(flow: FlowNode): Map { + if (!flow.id) { + flow.id = nextFlowId; + nextFlowId++; + } + return flowTypeCaches[flow.id] || (flowTypeCaches[flow.id] = {}); + } + + function isNarrowableReference(expr: Node): boolean { + return expr.kind === SyntaxKind.Identifier || + expr.kind === SyntaxKind.ThisKeyword || + expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); + } + + function getAssignmentReducedType(type: Type, assignedType: Type) { + if (type.flags & TypeFlags.Union) { + const reducedTypes = filter((type).types, t => isTypeAssignableTo(assignedType, t)); + if (reducedTypes.length) { + return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); + } + } + return type; + } + function getNarrowedTypeOfReference(type: Type, reference: Node) { if (!(type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) { return type; } + if (!isNarrowableReference(reference)) { + return type; + } const leftmostNode = getLeftmostIdentifierOrThis(reference); if (!leftmostNode) { return type; } - let top: Node; if (leftmostNode.kind === SyntaxKind.Identifier) { const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode)); if (!leftmostSymbol) { @@ -7318,74 +7260,138 @@ namespace ts { if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) { return type; } - top = getDeclarationContainer(declaration); } - const originalType = type; - const nodeStack: { node: Node, child: Node }[] = []; - let node: Node = reference; - loop: while (node.parent) { - const child = node; - node = node.parent; - switch (node.kind) { - case SyntaxKind.IfStatement: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.BinaryExpression: - nodeStack.push({node, child}); - break; - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - break loop; - default: - if (node === top || isFunctionLikeKind(node.kind)) { - break loop; + return getFlowTypeOfReference(reference, type, type); + } + + function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { + let key: string; + return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : initialType; + + function getTypeAtFlowNode(flow: FlowNode): Type { + while (true) { + switch (flow.kind) { + case FlowKind.Assignment: + const type = getTypeAtFlowAssignment(flow); + if (!type) { + flow = (flow).antecedent; + continue; + } + return type; + case FlowKind.Condition: + return getTypeAtFlowCondition(flow); + case FlowKind.Label: + if ((flow).antecedents.length === 1) { + flow = (flow).antecedents[0]; + continue; + } + return getTypeAtFlowLabel(flow); + } + // At the top of the flow we have the initial type + return initialType; + } + } + + function getTypeAtVariableDeclaration(node: VariableDeclaration) { + if (reference.kind === SyntaxKind.Identifier && !isBindingPattern(node.name) && getResolvedSymbol(reference) === getSymbolOfNode(node)) { + return getAssignmentReducedType(declaredType, checkExpressionCached((node).initializer)); + } + return undefined; + } + + function getTypeAtForInOrForOfStatement(node: ForInStatement | ForOfStatement) { + if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { + if (reference.kind === SyntaxKind.Identifier) { + const variable = (node.initializer).declarations[0]; + if (variable && !isBindingPattern(variable.name) && getResolvedSymbol(reference) === getSymbolOfNode(variable)) { + return declaredType; } - break; + } + } + else { + if (isMatchingReference(reference, node.initializer)) { + const type = node.kind === SyntaxKind.ForOfStatement ? checkRightHandSideOfForOf(node.expression) : stringType; + return getAssignmentReducedType(declaredType, type); + } + if (reference.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference((reference).expression, node.initializer)) { + return declaredType; + } } + return undefined; } - let nodes: { node: Node, child: Node }; - while (nodes = nodeStack.pop()) { - const {node, child} = nodes; + function getTypeAtFlowAssignment(flow: FlowAssignment) { + const node = flow.node; switch (node.kind) { - case SyntaxKind.IfStatement: - // In a branch of an if statement, narrow based on controlling expression - if (child !== (node).expression) { - type = narrowType(type, (node).expression, /*assumeTrue*/ child === (node).thenStatement); - } - break; - case SyntaxKind.ConditionalExpression: - // In a branch of a conditional expression, narrow based on controlling condition - if (child !== (node).condition) { - type = narrowType(type, (node).condition, /*assumeTrue*/ child === (node).whenTrue); - } - break; case SyntaxKind.BinaryExpression: - // In the right operand of an && or ||, narrow based on left operand - if (child === (node).right) { - if ((node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { - type = narrowType(type, (node).left, /*assumeTrue*/ true); - } - else if ((node).operatorToken.kind === SyntaxKind.BarBarToken) { - type = narrowType(type, (node).left, /*assumeTrue*/ false); - } + // If reference matches left hand side and type on right is properly assignable, + // return type on right. Otherwise default to the declared type. + if (isMatchingReference(reference, (node).left)) { + return getAssignmentReducedType(declaredType, checkExpressionCached((node).right)); + } + // We didn't have a direct match. However, if the reference is a dotted name, this + // may be an assignment to a left hand part of the reference. For example, for a + // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, + // return the declared type. + if (reference.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference((reference).expression, (node).left)) { + return declaredType; } break; - default: - Debug.fail("Unreachable!"); + case SyntaxKind.VariableDeclaration: + return getTypeAtVariableDeclaration(node); + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + return getTypeAtForInOrForOfStatement(node); } + // Assignment doesn't affect reference + return undefined; + } - // Use original type if construct contains assignments to variable - if (type !== originalType && isAnyPartOfReferenceAssignedWithin(reference, node)) { - type = originalType; + function getTypeAtFlowCondition(flow: FlowCondition) { + const type = getTypeAtFlowNode(flow.antecedent); + if (type === resolvingFlowType) { + return type; } + return narrowType(type, (flow).expression, (flow).assumeTrue); } - // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type - if (type === emptyUnionType) { - type = originalType; + function getTypeAtFlowNodeCached(flow: FlowNode) { + const cache = getFlowTypeCache(flow); + if (!key) { + key = getFlowCacheKey(reference); + } + let type = cache[key]; + if (type) { + return type; + } + cache[key] = resolvingFlowType; + type = getTypeAtFlowNode(flow); + cache[key] = type !== resolvingFlowType ? type : undefined; + return type; } - return type; + function getTypeAtFlowLabel(flow: FlowLabel) { + const antecedentTypes: Type[] = []; + for (const antecedent of flow.antecedents) { + const t = getTypeAtFlowNodeCached(antecedent); + if (t !== resolvingFlowType) { + // If the type at a particular antecedent path is the declared type, there is no + // reason to process more antecedents since the only possible outcome is subtypes + // that are be removed in the final union type anyway. + if (t === declaredType) { + return t; + } + if (!contains(antecedentTypes, t)) { + antecedentTypes.push(t); + } + } + } + return antecedentTypes.length === 0 ? declaredType : + antecedentTypes.length === 1 ? antecedentTypes[0] : + getUnionType(antecedentTypes); + } function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { return strictNullChecks && assumeTrue && isMatchingReference(expr, reference) ? getNonNullableType(type) : type; @@ -7574,7 +7580,7 @@ namespace ts { } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { - if (type.flags & TypeFlags.Any) { + if (type.flags & TypeFlags.Any || !hasMatchingArgument(callExpression, reference)) { return type; } const signature = getResolvedSignature(callExpression); @@ -7656,98 +7662,6 @@ namespace ts { return expression; } - function findFirstAssignment(symbol: Symbol, container: Node): Node { - return visit(isFunctionLike(container) ? (container).body : container); - - function visit(node: Node): Node { - switch (node.kind) { - case SyntaxKind.Identifier: - const assignment = getAssignmentRoot(node); - return assignment && getResolvedSymbol(node) === symbol ? assignment : undefined; - case SyntaxKind.BinaryExpression: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.DeleteExpression: - case SyntaxKind.AwaitExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.VoidExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.YieldExpression: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.VariableStatement: - case SyntaxKind.ExpressionStatement: - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseBlock: - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - case SyntaxKind.LabeledStatement: - case SyntaxKind.ThrowStatement: - case SyntaxKind.TryStatement: - case SyntaxKind.CatchClause: - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxExpression: - case SyntaxKind.Block: - case SyntaxKind.SourceFile: - return forEachChild(node, visit); - } - return undefined; - } - } - - function checkVariableAssignedBefore(symbol: Symbol, reference: Node) { - if (!(symbol.flags & SymbolFlags.Variable)) { - return; - } - const declaration = symbol.valueDeclaration; - if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration || (declaration).initializer) { - return; - } - const parentParentKind = declaration.parent.parent.kind; - if (parentParentKind === SyntaxKind.ForOfStatement || parentParentKind === SyntaxKind.ForInStatement) { - return; - } - const declarationContainer = getContainingFunction(declaration) || getSourceFileOfNode(declaration); - const referenceContainer = getContainingFunction(reference) || getSourceFileOfNode(reference); - if (declarationContainer !== referenceContainer) { - return; - } - const links = getSymbolLinks(symbol); - if (!links.firstAssignmentChecked) { - links.firstAssignmentChecked = true; - links.firstAssignment = findFirstAssignment(symbol, declarationContainer); - } - if (links.firstAssignment && links.firstAssignment.end <= reference.pos) { - return; - } - error(reference, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); - } - function checkIdentifier(node: Identifier): Type { const symbol = getResolvedSymbol(node); @@ -7800,10 +7714,18 @@ namespace ts { checkNestedBlockScopedBinding(node, symbol); const type = getTypeOfSymbol(localOrExportSymbol); - if (strictNullChecks && !isAssignmentTarget(node) && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined)) { - checkVariableAssignedBefore(symbol, node); + if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node)) { + return type; } - return getNarrowedTypeOfReference(type, node); + const declaration = localOrExportSymbol.valueDeclaration; + const defaultsToDeclaredType = !strictNullChecks || !declaration || + declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || + getContainingFunction(declaration) !== getContainingFunction(node); + const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); + if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { + error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); + } + return flowType; } function isInsideFunction(node: Node, threshold: Node): boolean { @@ -8715,8 +8637,10 @@ namespace ts { return mapper && mapper.context; } - // Return the root assignment node of an assignment target - function getAssignmentRoot(node: Node): Node { + // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property + // assignment in an object literal that is an assignment target, or if it is parented by an array literal that is + // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'. + function isAssignmentTarget(node: Node): boolean { while (node.parent.kind === SyntaxKind.ParenthesizedExpression) { node = node.parent; } @@ -8734,23 +8658,7 @@ namespace ts { const parent = node.parent; return parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && - (parent).left === node ? parent : undefined; - } - - // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property - // assignment in an object literal that is an assignment target, or if it is parented by an array literal that is - // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'. - function isAssignmentTarget(node: Node): boolean { - return !!getAssignmentRoot(node); - } - - function isCompoundAssignmentTarget(node: Node) { - const parent = node.parent; - if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node) { - const operator = (parent).operatorToken.kind; - return operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment; - } - return false; + (parent).left === node; } function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type { @@ -9604,7 +9512,7 @@ namespace ts { } const propType = getTypeOfSymbol(prop); - return node.kind === SyntaxKind.PropertyAccessExpression && prop.flags & SymbolFlags.Property ? + return node.kind === SyntaxKind.PropertyAccessExpression && prop.flags & SymbolFlags.Property && !isAssignmentTarget(node) ? getNarrowedTypeOfReference(propType, node) : propType; } @@ -16177,7 +16085,7 @@ namespace ts { } if (entityName.parent.kind === SyntaxKind.ExportAssignment) { - return resolveEntityName(entityName, + return resolveEntityName(entityName, /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 546d1631945cc..c850fe470128a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -449,6 +449,7 @@ namespace ts { /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) + /* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding) } export interface NodeArray extends Array, TextRange { @@ -1518,6 +1519,39 @@ namespace ts { isBracketed: boolean; } + export const enum FlowKind { + Unreachable, + Start, + Label, + Assignment, + Condition + } + + export interface FlowNode { + kind: FlowKind; // Node kind + id?: number; // Node id used by flow type cache in checker + } + + // FlowLabel represents a junction with multiple possible preceding control flows. + export interface FlowLabel extends FlowNode { + antecedents: FlowNode[]; + } + + // FlowAssignment represents a node that possibly assigns a value to one or more + // references. + export interface FlowAssignment extends FlowNode { + node: BinaryExpression | VariableDeclaration | ForInStatement | ForOfStatement; + antecedent: FlowNode; + } + + // FlowCondition represents a condition that is known to be true or false at the + // node's location in the control flow. + export interface FlowCondition extends FlowNode { + expression: Expression; + assumeTrue: boolean; + antecedent: FlowNode; + } + export interface AmdDependency { path: string; name: string; From afa1714c034f5d51c2151457cbf20d20ba239e84 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Mar 2016 10:22:08 -0700 Subject: [PATCH 02/67] Add type annotations to suppress circularity errors --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b7c011631ee18..3bbb86b58e056 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1811,7 +1811,7 @@ namespace ts { function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { let entity: EntityName = parseIdentifier(diagnosticMessage); while (parseOptional(SyntaxKind.DotToken)) { - const node = createNode(SyntaxKind.QualifiedName, entity.pos); + const node: QualifiedName = createNode(SyntaxKind.QualifiedName, entity.pos); // !!! node.left = entity; node.right = parseRightSideOfDot(allowReservedWords); entity = finishNode(node); @@ -3639,7 +3639,7 @@ namespace ts { let elementName: EntityName = parseIdentifierName(); while (parseOptional(SyntaxKind.DotToken)) { scanJsxIdentifier(); - const node = createNode(SyntaxKind.QualifiedName, elementName.pos); + const node: QualifiedName = createNode(SyntaxKind.QualifiedName, elementName.pos); // !!! node.left = elementName; node.right = parseIdentifierName(); elementName = finishNode(node); From 7c45c7ba9f211e382da31793f9c2e7ea4e5a43fa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Mar 2016 10:50:06 -0700 Subject: [PATCH 03/67] Fixing tests --- .../stringLiteralTypesInUnionTypes01.js | 8 ++-- .../stringLiteralTypesInUnionTypes01.symbols | 6 ++- .../stringLiteralTypesInUnionTypes01.types | 8 ++-- .../stringLiteralTypesInUnionTypes02.js | 8 ++-- .../stringLiteralTypesInUnionTypes02.symbols | 6 ++- .../stringLiteralTypesInUnionTypes02.types | 46 +++++++++---------- .../stringLiteralTypesInUnionTypes03.js | 4 +- .../stringLiteralTypesInUnionTypes03.symbols | 3 +- .../stringLiteralTypesInUnionTypes03.types | 4 +- .../stringLiteralTypesInUnionTypes04.js | 8 ++-- .../stringLiteralTypesInUnionTypes04.symbols | 6 ++- .../stringLiteralTypesInUnionTypes04.types | 8 ++-- .../stringLiteralTypesTypePredicates01.js | 4 +- ...stringLiteralTypesTypePredicates01.symbols | 3 +- .../stringLiteralTypesTypePredicates01.types | 4 +- .../stringLiteralTypesInUnionTypes01.ts | 4 +- .../stringLiteralTypesInUnionTypes02.ts | 4 +- .../stringLiteralTypesInUnionTypes03.ts | 2 +- .../stringLiteralTypesInUnionTypes04.ts | 4 +- .../stringLiteralTypesTypePredicates01.ts | 2 +- 20 files changed, 75 insertions(+), 67 deletions(-) diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.js b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.js index d970310d68e32..bdf06d393aafc 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.js +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.js @@ -2,8 +2,8 @@ type T = "foo" | "bar" | "baz"; -var x: "foo" | "bar" | "baz" = "foo"; -var y: T = "bar"; +var x: "foo" | "bar" | "baz" = undefined; +var y: T = undefined; if (x === "foo") { let a = x; @@ -21,8 +21,8 @@ x = y; y = x; //// [stringLiteralTypesInUnionTypes01.js] -var x = "foo"; -var y = "bar"; +var x = undefined; +var y = undefined; if (x === "foo") { var a = x; } diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.symbols b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.symbols index c608929383ed3..15a8db13cd034 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.symbols +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.symbols @@ -3,12 +3,14 @@ type T = "foo" | "bar" | "baz"; >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes01.ts, 0, 0)) -var x: "foo" | "bar" | "baz" = "foo"; +var x: "foo" | "bar" | "baz" = undefined; >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes01.ts, 3, 3)) +>undefined : Symbol(undefined) -var y: T = "bar"; +var y: T = undefined; >y : Symbol(y, Decl(stringLiteralTypesInUnionTypes01.ts, 4, 3)) >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes01.ts, 0, 0)) +>undefined : Symbol(undefined) if (x === "foo") { >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes01.ts, 3, 3)) diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types index e5ff509822b99..7201aaa91cab1 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types @@ -3,14 +3,14 @@ type T = "foo" | "bar" | "baz"; >T : "foo" | "bar" | "baz" -var x: "foo" | "bar" | "baz" = "foo"; +var x: "foo" | "bar" | "baz" = undefined; >x : "foo" | "bar" | "baz" ->"foo" : "foo" +>undefined : undefined -var y: T = "bar"; +var y: T = undefined; >y : "foo" | "bar" | "baz" >T : "foo" | "bar" | "baz" ->"bar" : "bar" +>undefined : undefined if (x === "foo") { >x === "foo" : boolean diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.js b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.js index 19b9c837c9da8..bca25e744c94b 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.js +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.js @@ -2,8 +2,8 @@ type T = string | "foo" | "bar" | "baz"; -var x: "foo" | "bar" | "baz" | string = "foo"; -var y: T = "bar"; +var x: "foo" | "bar" | "baz" | string = undefined; +var y: T = undefined; if (x === "foo") { let a = x; @@ -21,8 +21,8 @@ x = y; y = x; //// [stringLiteralTypesInUnionTypes02.js] -var x = "foo"; -var y = "bar"; +var x = undefined; +var y = undefined; if (x === "foo") { var a = x; } diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.symbols b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.symbols index c9b31dc710a02..c35b7a0691b5c 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.symbols +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.symbols @@ -3,12 +3,14 @@ type T = string | "foo" | "bar" | "baz"; >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes02.ts, 0, 0)) -var x: "foo" | "bar" | "baz" | string = "foo"; +var x: "foo" | "bar" | "baz" | string = undefined; >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes02.ts, 3, 3)) +>undefined : Symbol(undefined) -var y: T = "bar"; +var y: T = undefined; >y : Symbol(y, Decl(stringLiteralTypesInUnionTypes02.ts, 4, 3)) >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes02.ts, 0, 0)) +>undefined : Symbol(undefined) if (x === "foo") { >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes02.ts, 3, 3)) diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types index b468c620376ba..242248617e0d4 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types @@ -3,60 +3,60 @@ type T = string | "foo" | "bar" | "baz"; >T : string | "foo" | "bar" | "baz" -var x: "foo" | "bar" | "baz" | string = "foo"; +var x: "foo" | "bar" | "baz" | string = undefined; >x : "foo" | "bar" | "baz" | string ->"foo" : "foo" +>undefined : undefined -var y: T = "bar"; +var y: T = undefined; >y : string | "foo" | "bar" | "baz" >T : string | "foo" | "bar" | "baz" ->"bar" : "bar" +>undefined : undefined if (x === "foo") { >x === "foo" : boolean ->x : "foo" | "bar" | "baz" | string +>x : string >"foo" : string let a = x; ->a : "foo" | "bar" | "baz" | string ->x : "foo" | "bar" | "baz" | string +>a : string +>x : string } else if (x !== "bar") { >x !== "bar" : boolean ->x : "foo" | "bar" | "baz" | string +>x : string >"bar" : string let b = x || y; >b : string >x || y : string ->x : "foo" | "bar" | "baz" | string ->y : string | "foo" | "bar" | "baz" +>x : string +>y : string } else { let c = x; ->c : "foo" | "bar" | "baz" | string ->x : "foo" | "bar" | "baz" | string +>c : string +>x : string let d = y; ->d : string | "foo" | "bar" | "baz" ->y : string | "foo" | "bar" | "baz" +>d : string +>y : string let e: (typeof x) | (typeof y) = c || d; ->e : "foo" | "bar" | "baz" | string ->x : "foo" | "bar" | "baz" | string ->y : string | "foo" | "bar" | "baz" +>e : string +>x : string +>y : string >c || d : string ->c : "foo" | "bar" | "baz" | string ->d : string | "foo" | "bar" | "baz" +>c : string +>d : string } x = y; ->x = y : string | "foo" | "bar" | "baz" +>x = y : string >x : "foo" | "bar" | "baz" | string ->y : string | "foo" | "bar" | "baz" +>y : string y = x; ->y = x : "foo" | "bar" | "baz" | string +>y = x : string >y : string | "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" | string +>x : string diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.js b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.js index f6728e0b2fb2f..6264c99c13df0 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.js +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.js @@ -3,7 +3,7 @@ type T = number | "foo" | "bar"; var x: "foo" | "bar" | number; -var y: T = "bar"; +var y: T = undefined; if (x === "foo") { let a = x; @@ -22,7 +22,7 @@ y = x; //// [stringLiteralTypesInUnionTypes03.js] var x; -var y = "bar"; +var y = undefined; if (x === "foo") { var a = x; } diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.symbols b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.symbols index df5498d9a5943..6d519e2422518 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.symbols +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.symbols @@ -6,9 +6,10 @@ type T = number | "foo" | "bar"; var x: "foo" | "bar" | number; >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes03.ts, 3, 3)) -var y: T = "bar"; +var y: T = undefined; >y : Symbol(y, Decl(stringLiteralTypesInUnionTypes03.ts, 4, 3)) >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes03.ts, 0, 0)) +>undefined : Symbol(undefined) if (x === "foo") { >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes03.ts, 3, 3)) diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types index 5fca6e69be996..920f7e1a71ccb 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types @@ -6,10 +6,10 @@ type T = number | "foo" | "bar"; var x: "foo" | "bar" | number; >x : "foo" | "bar" | number -var y: T = "bar"; +var y: T = undefined; >y : number | "foo" | "bar" >T : number | "foo" | "bar" ->"bar" : "bar" +>undefined : undefined if (x === "foo") { >x === "foo" : boolean diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.js b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.js index dc6cf51ad1e1c..85c9a30c49d8a 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.js +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.js @@ -2,8 +2,8 @@ type T = "" | "foo"; -let x: T = ""; -let y: T = "foo"; +let x: T = undefined; +let y: T = undefined; if (x === "") { let a = x; @@ -38,8 +38,8 @@ if (!!!x) { } //// [stringLiteralTypesInUnionTypes04.js] -var x = ""; -var y = "foo"; +var x = undefined; +var y = undefined; if (x === "") { var a = x; } diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.symbols b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.symbols index ced93fcefc98f..9904fa8613fbd 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.symbols +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.symbols @@ -3,13 +3,15 @@ type T = "" | "foo"; >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes04.ts, 0, 0)) -let x: T = ""; +let x: T = undefined; >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes04.ts, 3, 3)) >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes04.ts, 0, 0)) +>undefined : Symbol(undefined) -let y: T = "foo"; +let y: T = undefined; >y : Symbol(y, Decl(stringLiteralTypesInUnionTypes04.ts, 4, 3)) >T : Symbol(T, Decl(stringLiteralTypesInUnionTypes04.ts, 0, 0)) +>undefined : Symbol(undefined) if (x === "") { >x : Symbol(x, Decl(stringLiteralTypesInUnionTypes04.ts, 3, 3)) diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types index ad92dc360d0a9..fdaaa21b6cbb2 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types @@ -3,15 +3,15 @@ type T = "" | "foo"; >T : "" | "foo" -let x: T = ""; +let x: T = undefined; >x : "" | "foo" >T : "" | "foo" ->"" : "" +>undefined : undefined -let y: T = "foo"; +let y: T = undefined; >y : "" | "foo" >T : "" | "foo" ->"foo" : "foo" +>undefined : undefined if (x === "") { >x === "" : boolean diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.js b/tests/baselines/reference/stringLiteralTypesTypePredicates01.js index fd4129d272d05..fdd03c83aa5b0 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.js +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.js @@ -8,7 +8,7 @@ function kindIs(kind: Kind, is: Kind): boolean { return kind === is; } -var x: Kind = "A"; +var x: Kind = undefined; if (kindIs(x, "A")) { let a = x; @@ -28,7 +28,7 @@ else { function kindIs(kind, is) { return kind === is; } -var x = "A"; +var x = undefined; if (kindIs(x, "A")) { var a = x; } diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols b/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols index 6b18cab743233..aea8c6ac262b2 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols @@ -29,9 +29,10 @@ function kindIs(kind: Kind, is: Kind): boolean { >is : Symbol(is, Decl(stringLiteralTypesTypePredicates01.ts, 5, 27)) } -var x: Kind = "A"; +var x: Kind = undefined; >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) >Kind : Symbol(Kind, Decl(stringLiteralTypesTypePredicates01.ts, 0, 0)) +>undefined : Symbol(undefined) if (kindIs(x, "A")) { >kindIs : Symbol(kindIs, Decl(stringLiteralTypesTypePredicates01.ts, 1, 21), Decl(stringLiteralTypesTypePredicates01.ts, 3, 50), Decl(stringLiteralTypesTypePredicates01.ts, 4, 50)) diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.types b/tests/baselines/reference/stringLiteralTypesTypePredicates01.types index 41da80afd3048..4a765ea531295 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.types +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.types @@ -30,10 +30,10 @@ function kindIs(kind: Kind, is: Kind): boolean { >is : "A" | "B" } -var x: Kind = "A"; +var x: Kind = undefined; >x : "A" | "B" >Kind : "A" | "B" ->"A" : "A" +>undefined : undefined if (kindIs(x, "A")) { >kindIs(x, "A") : boolean diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes01.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes01.ts index b8ed1b47dd404..05f8c5111180b 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes01.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes01.ts @@ -2,8 +2,8 @@ type T = "foo" | "bar" | "baz"; -var x: "foo" | "bar" | "baz" = "foo"; -var y: T = "bar"; +var x: "foo" | "bar" | "baz" = undefined; +var y: T = undefined; if (x === "foo") { let a = x; diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes02.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes02.ts index 2cc63ab44605d..ee61efc37cac5 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes02.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes02.ts @@ -2,8 +2,8 @@ type T = string | "foo" | "bar" | "baz"; -var x: "foo" | "bar" | "baz" | string = "foo"; -var y: T = "bar"; +var x: "foo" | "bar" | "baz" | string = undefined; +var y: T = undefined; if (x === "foo") { let a = x; diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes03.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes03.ts index d5c6a38af799c..96a4f035c4baa 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes03.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes03.ts @@ -3,7 +3,7 @@ type T = number | "foo" | "bar"; var x: "foo" | "bar" | number; -var y: T = "bar"; +var y: T = undefined; if (x === "foo") { let a = x; diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes04.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes04.ts index e9d062b0e5c20..9f37e272b460c 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes04.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesInUnionTypes04.ts @@ -2,8 +2,8 @@ type T = "" | "foo"; -let x: T = ""; -let y: T = "foo"; +let x: T = undefined; +let y: T = undefined; if (x === "") { let a = x; diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts index a78918c8122be..2199ff3a7c782 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts @@ -8,7 +8,7 @@ function kindIs(kind: Kind, is: Kind): boolean { return kind === is; } -var x: Kind = "A"; +var x: Kind = undefined; if (kindIs(x, "A")) { let a = x; From 80c2e5ead2904f4293d7200e6623c94bb76f970d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Mar 2016 10:57:56 -0700 Subject: [PATCH 04/67] Accepting new baselines --- .../nestedBlockScopedBindings13.errors.txt | 18 ++ .../nestedBlockScopedBindings13.symbols | 16 -- .../nestedBlockScopedBindings13.types | 23 -- .../nestedBlockScopedBindings14.errors.txt | 20 ++ .../nestedBlockScopedBindings14.symbols | 22 -- .../nestedBlockScopedBindings14.types | 29 --- .../nestedBlockScopedBindings15.errors.txt | 46 ++++ .../nestedBlockScopedBindings15.symbols | 46 ---- .../nestedBlockScopedBindings15.types | 66 ----- .../nestedBlockScopedBindings16.errors.txt | 50 ++++ .../nestedBlockScopedBindings16.symbols | 58 ----- .../nestedBlockScopedBindings16.types | 78 ------ .../nestedBlockScopedBindings5.errors.txt | 92 +++++++ .../nestedBlockScopedBindings5.symbols | 163 ------------- .../nestedBlockScopedBindings5.types | 227 ------------------ .../nestedBlockScopedBindings7.errors.txt | 16 ++ .../nestedBlockScopedBindings7.symbols | 14 -- .../nestedBlockScopedBindings7.types | 19 -- .../nestedBlockScopedBindings8.errors.txt | 18 ++ .../nestedBlockScopedBindings8.symbols | 20 -- .../nestedBlockScopedBindings8.types | 25 -- .../parser_duplicateLabel1.errors.txt | 5 +- .../parser_duplicateLabel2.errors.txt | 5 +- .../reference/reachabilityChecks5.errors.txt | 5 +- .../reference/reachabilityChecks6.errors.txt | 5 +- .../baselines/reference/typeGuardEnums.types | 4 +- .../reference/typeGuardNesting.types | 10 +- .../typeGuardOfFormExpr1AndExpr2.types | 10 +- .../reference/typeGuardOfFormNotExpr.types | 14 +- .../reference/typeGuardRedundancy.types | 2 +- .../typeofOperatorWithAnyOtherType.errors.txt | 16 +- .../typeofOperatorWithStringType.errors.txt | 16 +- 32 files changed, 324 insertions(+), 834 deletions(-) create mode 100644 tests/baselines/reference/nestedBlockScopedBindings13.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings13.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings13.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings14.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings14.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings14.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings15.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings15.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings15.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings16.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings16.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings16.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings5.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings5.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings5.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings7.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings7.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings7.types create mode 100644 tests/baselines/reference/nestedBlockScopedBindings8.errors.txt delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings8.symbols delete mode 100644 tests/baselines/reference/nestedBlockScopedBindings8.types diff --git a/tests/baselines/reference/nestedBlockScopedBindings13.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings13.errors.txt new file mode 100644 index 0000000000000..0421583cff9b2 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings13.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/nestedBlockScopedBindings13.ts(2,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings13.ts(7,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings13.ts (2 errors) ==== + for (; false;) { + let x; + ~~~ +!!! error TS7027: Unreachable code detected. + () => x; + } + + for (; false;) { + let y; + ~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings13.symbols b/tests/baselines/reference/nestedBlockScopedBindings13.symbols deleted file mode 100644 index a5bc7ed7866a9..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings13.symbols +++ /dev/null @@ -1,16 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings13.ts === -for (; false;) { - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings13.ts, 1, 7)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings13.ts, 1, 7)) -} - -for (; false;) { - let y; ->y : Symbol(y, Decl(nestedBlockScopedBindings13.ts, 6, 7)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings13.ts, 6, 7)) -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings13.types b/tests/baselines/reference/nestedBlockScopedBindings13.types deleted file mode 100644 index 2e7bc7de77ed5..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings13.types +++ /dev/null @@ -1,23 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings13.ts === -for (; false;) { ->false : boolean - - let x; ->x : any - - () => x; ->() => x : () => any ->x : any -} - -for (; false;) { ->false : boolean - - let y; ->y : any - - y = 1; ->y = 1 : number ->y : any ->1 : number -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings14.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings14.errors.txt new file mode 100644 index 0000000000000..1b5babb4c25a0 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings14.errors.txt @@ -0,0 +1,20 @@ +tests/cases/compiler/nestedBlockScopedBindings14.ts(3,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings14.ts(9,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings14.ts (2 errors) ==== + var x; + for (; false;) { + let x; + ~~~ +!!! error TS7027: Unreachable code detected. + () => x; + } + + var y; + for (; false;) { + let y; + ~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings14.symbols b/tests/baselines/reference/nestedBlockScopedBindings14.symbols deleted file mode 100644 index 4a13b296e88b2..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings14.symbols +++ /dev/null @@ -1,22 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings14.ts === -var x; ->x : Symbol(x, Decl(nestedBlockScopedBindings14.ts, 0, 3)) - -for (; false;) { - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings14.ts, 2, 7)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings14.ts, 2, 7)) -} - -var y; ->y : Symbol(y, Decl(nestedBlockScopedBindings14.ts, 6, 3)) - -for (; false;) { - let y; ->y : Symbol(y, Decl(nestedBlockScopedBindings14.ts, 8, 7)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings14.ts, 8, 7)) -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings14.types b/tests/baselines/reference/nestedBlockScopedBindings14.types deleted file mode 100644 index 966eaaaac7a88..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings14.types +++ /dev/null @@ -1,29 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings14.ts === -var x; ->x : any - -for (; false;) { ->false : boolean - - let x; ->x : any - - () => x; ->() => x : () => any ->x : any -} - -var y; ->y : any - -for (; false;) { ->false : boolean - - let y; ->y : any - - y = 1; ->y = 1 : number ->y : any ->1 : number -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings15.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings15.errors.txt new file mode 100644 index 0000000000000..b60a502b528d7 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings15.errors.txt @@ -0,0 +1,46 @@ +tests/cases/compiler/nestedBlockScopedBindings15.ts(3,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings15.ts(10,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings15.ts(16,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings15.ts(25,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings15.ts (4 errors) ==== + for (; false;) { + { + let x; + ~~~ +!!! error TS7027: Unreachable code detected. + () => x; + } + } + + for (; false;) { + { + let y; + ~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + } + } + + for (; false;) { + switch (1){ + ~~~~~~ +!!! error TS7027: Unreachable code detected. + case 1: + let z0; + () => z0; + break; + } + } + + for (; false;) { + switch (1){ + ~~~~~~ +!!! error TS7027: Unreachable code detected. + case 1: + let z; + z = 1; + break; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings15.symbols b/tests/baselines/reference/nestedBlockScopedBindings15.symbols deleted file mode 100644 index 26ef28f60507b..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings15.symbols +++ /dev/null @@ -1,46 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings15.ts === -for (; false;) { - { - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings15.ts, 2, 11)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings15.ts, 2, 11)) - } -} - -for (; false;) { - { - let y; ->y : Symbol(y, Decl(nestedBlockScopedBindings15.ts, 9, 11)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings15.ts, 9, 11)) - } -} - -for (; false;) { - switch (1){ - case 1: - let z0; ->z0 : Symbol(z0, Decl(nestedBlockScopedBindings15.ts, 17, 15)) - - () => z0; ->z0 : Symbol(z0, Decl(nestedBlockScopedBindings15.ts, 17, 15)) - - break; - } -} - -for (; false;) { - switch (1){ - case 1: - let z; ->z : Symbol(z, Decl(nestedBlockScopedBindings15.ts, 26, 15)) - - z = 1; ->z : Symbol(z, Decl(nestedBlockScopedBindings15.ts, 26, 15)) - - break; - } -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings15.types b/tests/baselines/reference/nestedBlockScopedBindings15.types deleted file mode 100644 index 5173c0c5600b1..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings15.types +++ /dev/null @@ -1,66 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings15.ts === -for (; false;) { ->false : boolean - { - let x; ->x : any - - () => x; ->() => x : () => any ->x : any - } -} - -for (; false;) { ->false : boolean - { - let y; ->y : any - - y = 1; ->y = 1 : number ->y : any ->1 : number - } -} - -for (; false;) { ->false : boolean - - switch (1){ ->1 : number - - case 1: ->1 : number - - let z0; ->z0 : any - - () => z0; ->() => z0 : () => any ->z0 : any - - break; - } -} - -for (; false;) { ->false : boolean - - switch (1){ ->1 : number - - case 1: ->1 : number - - let z; ->z : any - - z = 1; ->z = 1 : number ->z : any ->1 : number - - break; - } -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings16.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings16.errors.txt new file mode 100644 index 0000000000000..806f58231a060 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings16.errors.txt @@ -0,0 +1,50 @@ +tests/cases/compiler/nestedBlockScopedBindings16.ts(4,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings16.ts(12,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings16.ts(19,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings16.ts(29,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings16.ts (4 errors) ==== + var x; + for (; false;) { + { + let x; + ~~~ +!!! error TS7027: Unreachable code detected. + () => x; + } + } + + var y; + for (; false;) { + { + let y; + ~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + } + } + + var z0; + for (; false;) { + switch (1){ + ~~~~~~ +!!! error TS7027: Unreachable code detected. + case 1: + let z0; + () => z0; + break; + } + } + + var z; + for (; false;) { + switch (1){ + ~~~~~~ +!!! error TS7027: Unreachable code detected. + case 1: + let z; + z = 1; + break; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings16.symbols b/tests/baselines/reference/nestedBlockScopedBindings16.symbols deleted file mode 100644 index cbe8114cf14cc..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings16.symbols +++ /dev/null @@ -1,58 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings16.ts === -var x; ->x : Symbol(x, Decl(nestedBlockScopedBindings16.ts, 0, 3)) - -for (; false;) { - { - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings16.ts, 3, 11)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings16.ts, 3, 11)) - } -} - -var y; ->y : Symbol(y, Decl(nestedBlockScopedBindings16.ts, 8, 3)) - -for (; false;) { - { - let y; ->y : Symbol(y, Decl(nestedBlockScopedBindings16.ts, 11, 11)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings16.ts, 11, 11)) - } -} - -var z0; ->z0 : Symbol(z0, Decl(nestedBlockScopedBindings16.ts, 16, 3)) - -for (; false;) { - switch (1){ - case 1: - let z0; ->z0 : Symbol(z0, Decl(nestedBlockScopedBindings16.ts, 20, 15)) - - () => z0; ->z0 : Symbol(z0, Decl(nestedBlockScopedBindings16.ts, 20, 15)) - - break; - } -} - -var z; ->z : Symbol(z, Decl(nestedBlockScopedBindings16.ts, 26, 3)) - -for (; false;) { - switch (1){ - case 1: - let z; ->z : Symbol(z, Decl(nestedBlockScopedBindings16.ts, 30, 15)) - - z = 1; ->z : Symbol(z, Decl(nestedBlockScopedBindings16.ts, 30, 15)) - - break; - } -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings16.types b/tests/baselines/reference/nestedBlockScopedBindings16.types deleted file mode 100644 index 22698402a62df..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings16.types +++ /dev/null @@ -1,78 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings16.ts === -var x; ->x : any - -for (; false;) { ->false : boolean - { - let x; ->x : any - - () => x; ->() => x : () => any ->x : any - } -} - -var y; ->y : any - -for (; false;) { ->false : boolean - { - let y; ->y : any - - y = 1; ->y = 1 : number ->y : any ->1 : number - } -} - -var z0; ->z0 : any - -for (; false;) { ->false : boolean - - switch (1){ ->1 : number - - case 1: ->1 : number - - let z0; ->z0 : any - - () => z0; ->() => z0 : () => any ->z0 : any - - break; - } -} - -var z; ->z : any - -for (; false;) { ->false : boolean - - switch (1){ ->1 : number - - case 1: ->1 : number - - let z; ->z : any - - z = 1; ->z = 1 : number ->z : any ->1 : number - - break; - } -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings5.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings5.errors.txt new file mode 100644 index 0000000000000..a40f6a29aead0 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings5.errors.txt @@ -0,0 +1,92 @@ +tests/cases/compiler/nestedBlockScopedBindings5.ts(37,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings5.ts(54,9): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings5.ts(71,9): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings5.ts (3 errors) ==== + function a0() { + for (let x in []) { + x = x + 1; + } + for (let x;;) { + x = x + 2; + } + } + + function a1() { + for (let x in []) { + x = x + 1; + () => x; + } + for (let x;;) { + x = x + 2; + } + } + + function a2() { + for (let x in []) { + x = x + 1; + } + for (let x;;) { + x = x + 2; + () => x; + } + } + + + function a3() { + for (let x in []) { + x = x + 1; + () => x; + } + for (let x;false;) { + x = x + 2; + ~ +!!! error TS7027: Unreachable code detected. + () => x; + } + switch (1) { + case 1: + let x; + () => x; + break; + } + + } + + function a4() { + for (let x in []) { + x = x + 1; + } + for (let x;false;) { + x = x + 2; + ~ +!!! error TS7027: Unreachable code detected. + } + switch (1) { + case 1: + let x; + () => x; + break; + } + + } + + function a5() { + let y; + for (let x in []) { + x = x + 1; + } + for (let x;false;) { + x = x + 2; + ~ +!!! error TS7027: Unreachable code detected. + () => x; + } + switch (1) { + case 1: + let x; + break; + } + + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings5.symbols b/tests/baselines/reference/nestedBlockScopedBindings5.symbols deleted file mode 100644 index 202a1d2e83f06..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings5.symbols +++ /dev/null @@ -1,163 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings5.ts === -function a0() { ->a0 : Symbol(a0, Decl(nestedBlockScopedBindings5.ts, 0, 0)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 1, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 1, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 1, 12)) - } - for (let x;;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 4, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 4, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 4, 12)) - } -} - -function a1() { ->a1 : Symbol(a1, Decl(nestedBlockScopedBindings5.ts, 7, 1)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 10, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 10, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 10, 12)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 10, 12)) - } - for (let x;;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 14, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 14, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 14, 12)) - } -} - -function a2() { ->a2 : Symbol(a2, Decl(nestedBlockScopedBindings5.ts, 17, 1)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 20, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 20, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 20, 12)) - } - for (let x;;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 23, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 23, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 23, 12)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 23, 12)) - } -} - - -function a3() { ->a3 : Symbol(a3, Decl(nestedBlockScopedBindings5.ts, 27, 1)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 31, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 31, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 31, 12)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 31, 12)) - } - for (let x;false;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 35, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 35, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 35, 12)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 35, 12)) - } - switch (1) { - case 1: - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 41, 15)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 41, 15)) - - break; - } - -} - -function a4() { ->a4 : Symbol(a4, Decl(nestedBlockScopedBindings5.ts, 46, 1)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 49, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 49, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 49, 12)) - } - for (let x;false;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 52, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 52, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 52, 12)) - } - switch (1) { - case 1: - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 57, 15)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 57, 15)) - - break; - } - -} - -function a5() { ->a5 : Symbol(a5, Decl(nestedBlockScopedBindings5.ts, 62, 1)) - - let y; ->y : Symbol(y, Decl(nestedBlockScopedBindings5.ts, 65, 7)) - - for (let x in []) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 66, 12)) - - x = x + 1; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 66, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 66, 12)) - } - for (let x;false;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 69, 12)) - - x = x + 2; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 69, 12)) ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 69, 12)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 69, 12)) - } - switch (1) { - case 1: - let x; ->x : Symbol(x, Decl(nestedBlockScopedBindings5.ts, 75, 15)) - - break; - } - -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings5.types b/tests/baselines/reference/nestedBlockScopedBindings5.types deleted file mode 100644 index 10ea0e5f902fd..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings5.types +++ /dev/null @@ -1,227 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings5.ts === -function a0() { ->a0 : () => void - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - } - for (let x;;) { ->x : any - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - } -} - -function a1() { ->a1 : () => void - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - - () => x; ->() => x : () => string ->x : string - } - for (let x;;) { ->x : any - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - } -} - -function a2() { ->a2 : () => void - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - } - for (let x;;) { ->x : any - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - - () => x; ->() => x : () => any ->x : any - } -} - - -function a3() { ->a3 : () => void - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - - () => x; ->() => x : () => string ->x : string - } - for (let x;false;) { ->x : any ->false : boolean - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - - () => x; ->() => x : () => any ->x : any - } - switch (1) { ->1 : number - - case 1: ->1 : number - - let x; ->x : any - - () => x; ->() => x : () => any ->x : any - - break; - } - -} - -function a4() { ->a4 : () => void - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - } - for (let x;false;) { ->x : any ->false : boolean - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - } - switch (1) { ->1 : number - - case 1: ->1 : number - - let x; ->x : any - - () => x; ->() => x : () => any ->x : any - - break; - } - -} - -function a5() { ->a5 : () => void - - let y; ->y : any - - for (let x in []) { ->x : string ->[] : undefined[] - - x = x + 1; ->x = x + 1 : string ->x : string ->x + 1 : string ->x : string ->1 : number - } - for (let x;false;) { ->x : any ->false : boolean - - x = x + 2; ->x = x + 2 : any ->x : any ->x + 2 : any ->x : any ->2 : number - - () => x; ->() => x : () => any ->x : any - } - switch (1) { ->1 : number - - case 1: ->1 : number - - let x; ->x : any - - break; - } - -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings7.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings7.errors.txt new file mode 100644 index 0000000000000..274ef8bf6c336 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings7.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/nestedBlockScopedBindings7.ts(2,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings7.ts(6,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings7.ts (2 errors) ==== + for (let x; false;) { + () => x; + ~ +!!! error TS7027: Unreachable code detected. + } + + for (let y; false;) { + y = 1; + ~ +!!! error TS7027: Unreachable code detected. + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings7.symbols b/tests/baselines/reference/nestedBlockScopedBindings7.symbols deleted file mode 100644 index 2d65af06a960e..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings7.symbols +++ /dev/null @@ -1,14 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings7.ts === -for (let x; false;) { ->x : Symbol(x, Decl(nestedBlockScopedBindings7.ts, 0, 8)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings7.ts, 0, 8)) -} - -for (let y; false;) { ->y : Symbol(y, Decl(nestedBlockScopedBindings7.ts, 4, 8)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings7.ts, 4, 8)) -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings7.types b/tests/baselines/reference/nestedBlockScopedBindings7.types deleted file mode 100644 index 2fdb2d90e76a0..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings7.types +++ /dev/null @@ -1,19 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings7.ts === -for (let x; false;) { ->x : any ->false : boolean - - () => x; ->() => x : () => any ->x : any -} - -for (let y; false;) { ->y : any ->false : boolean - - y = 1; ->y = 1 : number ->y : any ->1 : number -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings8.errors.txt b/tests/baselines/reference/nestedBlockScopedBindings8.errors.txt new file mode 100644 index 0000000000000..73f580ae3cb74 --- /dev/null +++ b/tests/baselines/reference/nestedBlockScopedBindings8.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/nestedBlockScopedBindings8.ts(3,5): error TS7027: Unreachable code detected. +tests/cases/compiler/nestedBlockScopedBindings8.ts(8,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/nestedBlockScopedBindings8.ts (2 errors) ==== + var x; + for (let x; false; ) { + () => x; + ~ +!!! error TS7027: Unreachable code detected. + } + + var y; + for (let y; false; ) { + y = 1; + ~ +!!! error TS7027: Unreachable code detected. + } \ No newline at end of file diff --git a/tests/baselines/reference/nestedBlockScopedBindings8.symbols b/tests/baselines/reference/nestedBlockScopedBindings8.symbols deleted file mode 100644 index a61df0502ec8a..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings8.symbols +++ /dev/null @@ -1,20 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings8.ts === -var x; ->x : Symbol(x, Decl(nestedBlockScopedBindings8.ts, 0, 3)) - -for (let x; false; ) { ->x : Symbol(x, Decl(nestedBlockScopedBindings8.ts, 1, 8)) - - () => x; ->x : Symbol(x, Decl(nestedBlockScopedBindings8.ts, 1, 8)) -} - -var y; ->y : Symbol(y, Decl(nestedBlockScopedBindings8.ts, 5, 3)) - -for (let y; false; ) { ->y : Symbol(y, Decl(nestedBlockScopedBindings8.ts, 6, 8)) - - y = 1; ->y : Symbol(y, Decl(nestedBlockScopedBindings8.ts, 6, 8)) -} diff --git a/tests/baselines/reference/nestedBlockScopedBindings8.types b/tests/baselines/reference/nestedBlockScopedBindings8.types deleted file mode 100644 index 9e4b98ffbf2f3..0000000000000 --- a/tests/baselines/reference/nestedBlockScopedBindings8.types +++ /dev/null @@ -1,25 +0,0 @@ -=== tests/cases/compiler/nestedBlockScopedBindings8.ts === -var x; ->x : any - -for (let x; false; ) { ->x : any ->false : boolean - - () => x; ->() => x : () => any ->x : any -} - -var y; ->y : any - -for (let y; false; ) { ->y : any ->false : boolean - - y = 1; ->y = 1 : number ->y : any ->1 : number -} diff --git a/tests/baselines/reference/parser_duplicateLabel1.errors.txt b/tests/baselines/reference/parser_duplicateLabel1.errors.txt index a4319aaa7b9e9..c98ede21ecf16 100644 --- a/tests/baselines/reference/parser_duplicateLabel1.errors.txt +++ b/tests/baselines/reference/parser_duplicateLabel1.errors.txt @@ -1,13 +1,16 @@ tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts(1,1): error TS7028: Unused label. tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts(2,1): error TS1114: Duplicate label 'target' +tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts(2,1): error TS7028: Unused label. -==== tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts (3 errors) ==== target: ~~~~~~ !!! error TS7028: Unused label. target: ~~~~~~ !!! error TS1114: Duplicate label 'target' + ~~~~~~ +!!! error TS7028: Unused label. while (true) { } \ No newline at end of file diff --git a/tests/baselines/reference/parser_duplicateLabel2.errors.txt b/tests/baselines/reference/parser_duplicateLabel2.errors.txt index 123949fa5aab3..cbad8b73a2106 100644 --- a/tests/baselines/reference/parser_duplicateLabel2.errors.txt +++ b/tests/baselines/reference/parser_duplicateLabel2.errors.txt @@ -1,8 +1,9 @@ tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts(1,1): error TS7028: Unused label. tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts(3,3): error TS1114: Duplicate label 'target' +tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts(3,3): error TS7028: Unused label. -==== tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts (3 errors) ==== target: ~~~~~~ !!! error TS7028: Unused label. @@ -10,6 +11,8 @@ tests/cases/conformance/parser/ecmascript5/Statements/LabeledStatements/parser_d target: ~~~~~~ !!! error TS1114: Duplicate label 'target' + ~~~~~~ +!!! error TS7028: Unused label. while (true) { } } \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks5.errors.txt b/tests/baselines/reference/reachabilityChecks5.errors.txt index 147dbe264a36c..4a894261a3d59 100644 --- a/tests/baselines/reference/reachabilityChecks5.errors.txt +++ b/tests/baselines/reference/reachabilityChecks5.errors.txt @@ -6,11 +6,12 @@ tests/cases/compiler/reachabilityChecks5.ts(52,17): error TS7030: Not all code p tests/cases/compiler/reachabilityChecks5.ts(80,17): error TS7030: Not all code paths return a value. tests/cases/compiler/reachabilityChecks5.ts(86,13): error TS7027: Unreachable code detected. tests/cases/compiler/reachabilityChecks5.ts(94,17): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks5.ts(97,13): error TS7027: Unreachable code detected. tests/cases/compiler/reachabilityChecks5.ts(116,18): error TS7030: Not all code paths return a value. tests/cases/compiler/reachabilityChecks5.ts(123,13): error TS7027: Unreachable code detected. -==== tests/cases/compiler/reachabilityChecks5.ts (10 errors) ==== +==== tests/cases/compiler/reachabilityChecks5.ts (11 errors) ==== function f0(x): number { while (true); @@ -124,6 +125,8 @@ tests/cases/compiler/reachabilityChecks5.ts(123,13): error TS7027: Unreachable c try { while (false) { return 1; + ~~~~~~ +!!! error TS7027: Unreachable code detected. } } catch (e) { diff --git a/tests/baselines/reference/reachabilityChecks6.errors.txt b/tests/baselines/reference/reachabilityChecks6.errors.txt index 38e72def8c680..24832d9ab3892 100644 --- a/tests/baselines/reference/reachabilityChecks6.errors.txt +++ b/tests/baselines/reference/reachabilityChecks6.errors.txt @@ -5,11 +5,12 @@ tests/cases/compiler/reachabilityChecks6.ts(52,10): error TS7030: Not all code p tests/cases/compiler/reachabilityChecks6.ts(80,10): error TS7030: Not all code paths return a value. tests/cases/compiler/reachabilityChecks6.ts(86,13): error TS7027: Unreachable code detected. tests/cases/compiler/reachabilityChecks6.ts(94,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(97,13): error TS7027: Unreachable code detected. tests/cases/compiler/reachabilityChecks6.ts(116,10): error TS7030: Not all code paths return a value. tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable code detected. -==== tests/cases/compiler/reachabilityChecks6.ts (9 errors) ==== +==== tests/cases/compiler/reachabilityChecks6.ts (10 errors) ==== function f0(x) { while (true); @@ -121,6 +122,8 @@ tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable c try { while (false) { return 1; + ~~~~~~ +!!! error TS7027: Unreachable code detected. } } catch (e) { diff --git a/tests/baselines/reference/typeGuardEnums.types b/tests/baselines/reference/typeGuardEnums.types index 1d39a81d78acd..2bef691504606 100644 --- a/tests/baselines/reference/typeGuardEnums.types +++ b/tests/baselines/reference/typeGuardEnums.types @@ -27,7 +27,7 @@ else { if (typeof x !== "number") { >typeof x !== "number" : boolean >typeof x : string ->x : number | string | E | V +>x : number | string >"number" : string x; // string @@ -35,6 +35,6 @@ if (typeof x !== "number") { } else { x; // number|E|V ->x : number | E | V +>x : number } diff --git a/tests/baselines/reference/typeGuardNesting.types b/tests/baselines/reference/typeGuardNesting.types index 255e96da89eb8..2b18e232412e9 100644 --- a/tests/baselines/reference/typeGuardNesting.types +++ b/tests/baselines/reference/typeGuardNesting.types @@ -34,7 +34,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : boolean | string +>strOrBool : string | boolean >'boolean' : string >strOrBool : boolean >false : boolean @@ -56,7 +56,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : boolean | string +>strOrBool : string | boolean >'string' : string >strOrBool : boolean >false : boolean @@ -68,7 +68,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >typeof strOrBool !== 'string' && !strOrBool : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'string' : string >!strOrBool : boolean >strOrBool : boolean @@ -94,7 +94,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : boolean | string +>strOrBool : string | boolean >'boolean' : string >strOrBool : boolean >false : boolean @@ -116,7 +116,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : boolean | string +>strOrBool : string | boolean >'string' : string >strOrBool : boolean >false : boolean diff --git a/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types index 10f50bed52e94..a10d398988fd9 100644 --- a/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types +++ b/tests/baselines/reference/typeGuardOfFormExpr1AndExpr2.types @@ -95,11 +95,11 @@ if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "numbe >typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "number" : boolean >typeof strOrNumOrBoolOrC !== "string" : boolean >typeof strOrNumOrBoolOrC : string ->strOrNumOrBoolOrC : string | number | boolean | C +>strOrNumOrBoolOrC : C | string | number | boolean >"string" : string >typeof strOrNumOrBoolOrC !== "number" : boolean >typeof strOrNumOrBoolOrC : string ->strOrNumOrBoolOrC : number | boolean | C +>strOrNumOrBoolOrC : C | number | boolean >"number" : string >typeof strOrNumOrBool === "boolean" : boolean >typeof strOrNumOrBool : string @@ -107,9 +107,9 @@ if (typeof strOrNumOrBoolOrC !== "string" && typeof strOrNumOrBoolOrC !== "numbe >"boolean" : string cOrBool = strOrNumOrBoolOrC; // C | boolean ->cOrBool = strOrNumOrBoolOrC : boolean | C +>cOrBool = strOrNumOrBoolOrC : C | boolean >cOrBool : C | boolean ->strOrNumOrBoolOrC : boolean | C +>strOrNumOrBoolOrC : C | boolean bool = strOrNumOrBool; // boolean >bool = strOrNumOrBool : boolean @@ -120,7 +120,7 @@ else { var r1: string | number | boolean | C = strOrNumOrBoolOrC; // string | number | boolean | C >r1 : string | number | boolean | C >C : C ->strOrNumOrBoolOrC : string | number | boolean | C +>strOrNumOrBoolOrC : string | number | C | boolean var r2: string | number | boolean = strOrNumOrBool; >r2 : string | number | boolean diff --git a/tests/baselines/reference/typeGuardOfFormNotExpr.types b/tests/baselines/reference/typeGuardOfFormNotExpr.types index a99db08efab5b..e7cfd6a0dce0f 100644 --- a/tests/baselines/reference/typeGuardOfFormNotExpr.types +++ b/tests/baselines/reference/typeGuardOfFormNotExpr.types @@ -73,13 +73,13 @@ if (!(typeof strOrNumOrBool !== "string") || !(typeof strOrNumOrBool !== "number >(typeof strOrNumOrBool !== "string") : boolean >typeof strOrNumOrBool !== "string" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : boolean | string | number >"string" : string >!(typeof strOrNumOrBool !== "number") : boolean >(typeof strOrNumOrBool !== "number") : boolean >typeof strOrNumOrBool !== "number" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : number | boolean +>strOrNumOrBool : boolean | number >"number" : string strOrNum = strOrNumOrBool; // string | number @@ -152,19 +152,19 @@ if (!(typeof strOrNumOrBool === "string") && numOrBool !== strOrNumOrBool) { >(typeof strOrNumOrBool === "string") : boolean >typeof strOrNumOrBool === "string" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : boolean | string | number >"string" : string >numOrBool !== strOrNumOrBool : boolean >numOrBool : number | boolean ->strOrNumOrBool : number | boolean +>strOrNumOrBool : boolean | number numOrBool = strOrNumOrBool; // number | boolean ->numOrBool = strOrNumOrBool : number | boolean +>numOrBool = strOrNumOrBool : boolean | number >numOrBool : number | boolean ->strOrNumOrBool : number | boolean +>strOrNumOrBool : boolean | number } else { var r1: string | number | boolean = strOrNumOrBool; // string | number | boolean >r1 : string | number | boolean ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : string | boolean | number } diff --git a/tests/baselines/reference/typeGuardRedundancy.types b/tests/baselines/reference/typeGuardRedundancy.types index 1507ceb850ef3..754019de7ed7a 100644 --- a/tests/baselines/reference/typeGuardRedundancy.types +++ b/tests/baselines/reference/typeGuardRedundancy.types @@ -48,7 +48,7 @@ var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed; >typeof x === "string" || typeof x === "string" : boolean >typeof x === "string" : boolean >typeof x : string ->x : string | number +>x : number | string >"string" : string >typeof x === "string" : boolean >typeof x : string diff --git a/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt index 0139c96b3f998..f9394c622c0ec 100644 --- a/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt @@ -4,9 +4,13 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperator tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(68,1): error TS7028: Unused label. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(69,1): error TS7028: Unused label. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(70,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(71,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(72,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(73,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(74,1): error TS7028: Unused label. -==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts (6 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts (10 errors) ==== // typeof operator on any type var ANY: any; @@ -90,6 +94,14 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperator ~ !!! error TS7028: Unused label. z: typeof objA.a; + ~ +!!! error TS7028: Unused label. z: typeof A.foo; + ~ +!!! error TS7028: Unused label. z: typeof M.n; - z: typeof obj1.x; \ No newline at end of file + ~ +!!! error TS7028: Unused label. + z: typeof obj1.x; + ~ +!!! error TS7028: Unused label. \ No newline at end of file diff --git a/tests/baselines/reference/typeofOperatorWithStringType.errors.txt b/tests/baselines/reference/typeofOperatorWithStringType.errors.txt index 37b2cafab370c..bd891c949a36e 100644 --- a/tests/baselines/reference/typeofOperatorWithStringType.errors.txt +++ b/tests/baselines/reference/typeofOperatorWithStringType.errors.txt @@ -1,9 +1,13 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(50,1): error TS7028: Unused label. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(51,1): error TS7028: Unused label. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(52,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(54,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(55,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(56,1): error TS7028: Unused label. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts(57,1): error TS7028: Unused label. -==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts (3 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithStringType.ts (7 errors) ==== // typeof operator on string type var STRING: string; var STRING1: string[] = ["", "abc"]; @@ -64,6 +68,14 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperator !!! error TS7028: Unused label. var y = { a: "", b: "" }; z: typeof y.a; + ~ +!!! error TS7028: Unused label. z: typeof objA.a; + ~ +!!! error TS7028: Unused label. z: typeof A.foo; - z: typeof M.n; \ No newline at end of file + ~ +!!! error TS7028: Unused label. + z: typeof M.n; + ~ +!!! error TS7028: Unused label. \ No newline at end of file From 33985b24b7eb6b00ca256025f445addb9db67066 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Mar 2016 06:50:01 -0700 Subject: [PATCH 05/67] Adding a few optimizations --- src/compiler/checker.ts | 10 +++++----- src/compiler/types.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index da5cc90caa41c..7540482183d72 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7231,7 +7231,7 @@ namespace ts { } function getAssignmentReducedType(type: Type, assignedType: Type) { - if (type.flags & TypeFlags.Union) { + if (type !== assignedType && type.flags & TypeFlags.Union) { const reducedTypes = filter((type).types, t => isTypeAssignableTo(assignedType, t)); if (reducedTypes.length) { return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); @@ -7241,10 +7241,7 @@ namespace ts { } function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (!(type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) { - return type; - } - if (!isNarrowableReference(reference)) { + if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { return type; } const leftmostNode = getLeftmostIdentifierOrThis(reference); @@ -7721,6 +7718,9 @@ namespace ts { const defaultsToDeclaredType = !strictNullChecks || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunction(declaration) !== getContainingFunction(node); + if (defaultsToDeclaredType && !(type.flags & TypeFlags.Narrowable)) { + return type; + } const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c850fe470128a..ed82d65d5dd01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2176,6 +2176,7 @@ namespace ts { ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, + Narrowable = Any | ObjectType | Union | TypeParameter, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, /* @internal */ From ed5002c81a1c231f0184e3b40ec00fb54c15750e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Mar 2016 22:03:53 -0700 Subject: [PATCH 06/67] Handle assignment of union types in getAssignmentReducedType --- src/compiler/checker.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7540482183d72..ff37cd9f2b7d0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7230,14 +7230,29 @@ namespace ts { expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); } - function getAssignmentReducedType(type: Type, assignedType: Type) { - if (type !== assignedType && type.flags & TypeFlags.Union) { - const reducedTypes = filter((type).types, t => isTypeAssignableTo(assignedType, t)); + function typeMaybeAssignableTo(source: Type, target: Type) { + if (!(source.flags & TypeFlags.Union)) { + return isTypeAssignableTo(source, target); + } + for (const t of (source).types) { + if (isTypeAssignableTo(t, target)) { + return true; + } + } + return false; + } + + // Remove those constituent types of currentType to which no constituent type of assignedType is assignable. + // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, + // we remove type string. + function getAssignmentReducedType(currentType: Type, assignedType: Type) { + if (currentType !== assignedType && currentType.flags & TypeFlags.Union) { + const reducedTypes = filter((currentType).types, t => typeMaybeAssignableTo(assignedType, t)); if (reducedTypes.length) { return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); } } - return type; + return currentType; } function getNarrowedTypeOfReference(type: Type, reference: Node) { From 6d25a42fd99115ddf32c378a37a1d086174c470f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Mar 2016 22:04:44 -0700 Subject: [PATCH 07/67] Remove incorrect type predicate (could be true even when result is false) --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2966af3cb76f3..da33c18dfb812 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1412,7 +1412,7 @@ namespace ts { } // True if the given identifier, string literal, or number literal is the name of a declaration node - export function isDeclarationName(name: Node): name is Identifier | StringLiteral | LiteralExpression { + export function isDeclarationName(name: Node): boolean { if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) { return false; } From bf78470ed36ff629ffad3142b7a6ffd5f875936d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 25 Mar 2016 10:47:04 -0700 Subject: [PATCH 08/67] Fix overly aggressive optimization --- src/compiler/checker.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff37cd9f2b7d0..9ae6faeff74c2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7242,17 +7242,17 @@ namespace ts { return false; } - // Remove those constituent types of currentType to which no constituent type of assignedType is assignable. + // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. - function getAssignmentReducedType(currentType: Type, assignedType: Type) { - if (currentType !== assignedType && currentType.flags & TypeFlags.Union) { - const reducedTypes = filter((currentType).types, t => typeMaybeAssignableTo(assignedType, t)); + function getAssignmentReducedType(declaredType: Type, assignedType: Type) { + if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) { + const reducedTypes = filter((declaredType).types, t => typeMaybeAssignableTo(assignedType, t)); if (reducedTypes.length) { return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); } } - return currentType; + return declaredType; } function getNarrowedTypeOfReference(type: Type, reference: Node) { @@ -7389,10 +7389,11 @@ namespace ts { for (const antecedent of flow.antecedents) { const t = getTypeAtFlowNodeCached(antecedent); if (t !== resolvingFlowType) { - // If the type at a particular antecedent path is the declared type, there is no - // reason to process more antecedents since the only possible outcome is subtypes - // that are be removed in the final union type anyway. - if (t === declaredType) { + // If the type at a particular antecedent path is the declared type and the + // reference is known to always be assigned (i.e. when declared and initial types + // are the same), there is no reason to process more antecedents since the only + // possible outcome is subtypes that will be removed in the final union type anyway. + if (t === declaredType && declaredType === initialType) { return t; } if (!contains(antecedentTypes, t)) { From 4f936c468b89eeded5e87fc71232220946861021 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 25 Mar 2016 21:29:58 +0100 Subject: [PATCH 09/67] Add control flow tests --- .../controlFlowAssignmentExpression.ts | 10 +++ .../controlFlowBinaryAndExpression.ts | 9 +++ .../controlFlowBinaryOrExpression.ts | 9 +++ .../controlFlowConditionalExpression.ts | 5 ++ .../controlFlowDoWhileStatement.ts | 76 +++++++++++++++++++ .../controlFlow/controlFlowForInStatement.ts | 17 +++++ .../controlFlow/controlFlowForOfStatement.ts | 10 +++ .../controlFlow/controlFlowForStatement.ts | 41 ++++++++++ .../controlFlow/controlFlowIfStatement.ts | 36 +++++++++ .../controlFlow/controlFlowWhileStatement.ts | 75 ++++++++++++++++++ .../assignmentTypeNarrowing.ts | 28 +++++++ .../typeGuards/typeGuardsInDoStatement.ts | 27 +++++++ .../typeGuards/typeGuardsInForStatement.ts | 21 +++++ .../typeGuards/typeGuardsInWhileStatement.ts | 24 ++++++ 14 files changed, 388 insertions(+) create mode 100644 tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForInStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowIfStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts create mode 100644 tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts diff --git a/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts b/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts new file mode 100644 index 0000000000000..83bf75ab94d0c --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts @@ -0,0 +1,10 @@ +let x: string | boolean | number; +let obj: any; + +x = ""; +x = x.length; +x; // number + +x = true; +(x = "", obj).foo = (x = x.length); +x; // number diff --git a/tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts b/tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts new file mode 100644 index 0000000000000..caaef9b890f52 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts @@ -0,0 +1,9 @@ +let x: string | number | boolean; +let cond: boolean; + +(x = "") && (x = 0); +x; // string | number + +x = ""; +cond && (x = 0); +x; // string | number diff --git a/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts b/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts new file mode 100644 index 0000000000000..75b24622e0028 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts @@ -0,0 +1,9 @@ +let x: string | number | boolean; +let cond: boolean; + +(x = "") || (x = 0); +x; // string | number + +x = ""; +cond || (x = 0); +x; // string | number diff --git a/tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts b/tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts new file mode 100644 index 0000000000000..c1c1d9956ea05 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts @@ -0,0 +1,5 @@ +let x: string | number | boolean; +let cond: boolean; + +cond ? x = "" : x = 3; +x; // string | number diff --git a/tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts new file mode 100644 index 0000000000000..bd253bc064def --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts @@ -0,0 +1,76 @@ +let cond: boolean; +function a() { + let x: string | number; + x = ""; + do { + x; // string + } while (cond) +} +function b() { + let x: string | number; + x = ""; + do { + x; // string + x = 42; + break; + } while (cond) +} +function c() { + let x: string | number; + x = ""; + do { + x; // string + x = undefined; + if (typeof x === "string") continue; + break; + } while (cond) +} +function d() { + let x: string | number; + x = 1000; + do { + x; // number + x = ""; + } while (x = x.length) + x; // number +} +function e() { + let x: string | number; + x = ""; + do { + x = 42; + } while (cond) + x; // number +} +function f() { + let x: string | number | boolean | RegExp | Function; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (cond) + x; // number | boolean | RegExp +} +function g() { + let x: string | number | boolean | RegExp | Function; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (true) + x; // number +} diff --git a/tests/cases/conformance/controlFlow/controlFlowForInStatement.ts b/tests/cases/conformance/controlFlow/controlFlowForInStatement.ts new file mode 100644 index 0000000000000..a22e79506c250 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowForInStatement.ts @@ -0,0 +1,17 @@ +let x: string | number | boolean | RegExp | Function; +let obj: any; +let cond: boolean; + +x = /a/; +for (let y in obj) { + x = y; + if (cond) { + x = 42; + continue; + } + if (cond) { + x = true; + break; + } +} +x; // RegExp | string | number | boolean diff --git a/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts new file mode 100644 index 0000000000000..ac4c584e1dcae --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts @@ -0,0 +1,10 @@ +let obj: number[]; +let x: string | number | boolean | RegExp; + +function a() { + x = true; + for (x of obj) { + x = x.toExponential(); + } + x; // number | boolean +} diff --git a/tests/cases/conformance/controlFlow/controlFlowForStatement.ts b/tests/cases/conformance/controlFlow/controlFlowForStatement.ts new file mode 100644 index 0000000000000..d9e46781aa7b5 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowForStatement.ts @@ -0,0 +1,41 @@ +let cond: boolean; +function a() { + let x: string | number | boolean; + for (x = ""; cond; x = 5) { + x; // string | number + } +} +function b() { + let x: string | number | boolean; + for (x = 5; cond; x = x.length) { + x; // number + x = ""; + } +} +function c() { + let x: string | number | boolean; + for (x = 5; x = x.toExponential(); x = 5) { + x; // string + } +} +function d() { + let x: string | number | boolean; + for (x = ""; typeof x === "string"; x = 5) { + x; // string + } +} +function e() { + let x: string | number | boolean | RegExp; + for (x = "" || 0; typeof x !== "string"; x = "" || true) { + x; // number | boolean + } +} +function f() { + let x: string | number | boolean; + for (; typeof x !== "string";) { + x; // number | boolean + if (typeof x === "number") break; + x = undefined; + } + x; // string | number +} diff --git a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts new file mode 100644 index 0000000000000..c9e9be92f8e7e --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts @@ -0,0 +1,36 @@ +let x: string | number | boolean | RegExp; +let cond: boolean; + +x = /a/; +if (x /* RegExp */, (x = true)) { + x; // boolean + x = ""; +} +else { + x; // boolean + x = 42; +} +x; // string | number + +function a() { + let x: string | number; + if (cond) { + x = 42; + } + else { + x = ""; + return; + } + x; // number +} +function b() { + let x: string | number; + if (cond) { + x = 42; + throw ""; + } + else { + x = ""; + } + x; // string +} diff --git a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts new file mode 100644 index 0000000000000..697a867b88689 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts @@ -0,0 +1,75 @@ +let cond: boolean; +function a() { + let x: string | number; + x = ""; + while (cond) { + x; // string + } +} +function b() { + let x: string | number; + x = ""; + while (cond) { + x; // string + x = 42; + break; + } +} +function c() { + let x: string | number; + x = ""; + while (cond) { + x; // string + x = undefined; + if (typeof x === "string") continue; + break; + } +} +function d() { + let x: string | number; + x = ""; + while (x = x.length) { + x; // number + x = ""; + } +} +function e() { + let x: string | number; + x = ""; + while (cond) { + x = 42; + } + x; // string | number +} +function f() { + let x: string | number | boolean | RegExp | Function; + x = ""; + while (cond) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // string | number | boolean | RegExp +} +function g() { + let x: string | number | boolean | RegExp | Function; + x = ""; + while (true) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // number +} diff --git a/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts b/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts new file mode 100644 index 0000000000000..0e5e257635a57 --- /dev/null +++ b/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts @@ -0,0 +1,28 @@ +let x: string | number | boolean | RegExp; + +x = ""; +x; // string + +[x] = [true]; +x; // boolean + +[x = ""] = [1]; +x; // string | number + +({x} = {x: true}); +x; // boolean + +({y: x} = {y: 1}); +x; // number + +({x = ""} = {x: true}); +x; // string | boolean + +({y: x = /a/} = {y: 1}); +x; // number | RegExp + +let a: string[]; + +for (x of a) { + x; // string +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts new file mode 100644 index 0000000000000..1afbe515df8d4 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts @@ -0,0 +1,27 @@ +let cond: boolean; +function a(x: string | number | boolean) { + x = true; + do { + x; // boolean | string + x = undefined; + } while (typeof x === "string") + x; // number | boolean +} +function b(x: string | number | boolean) { + x = true; + do { + x; // boolean | string + if (cond) continue; + x = undefined; + } while (typeof x === "string") + x; // number | boolean +} +function c(x: string | number) { + x = ""; + do { + x; // string + if (cond) break; + x = undefined; + } while (typeof x === "string") + x; // string | number +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts new file mode 100644 index 0000000000000..cf5c008e80050 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts @@ -0,0 +1,21 @@ +let cond: boolean; +function a(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + } + x; // number +} +function b(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) continue; + } + x; // number +} +function c(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) break; + } + x; // string | number +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts new file mode 100644 index 0000000000000..642c899579531 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts @@ -0,0 +1,24 @@ +let cond: boolean; +function a(x: string | number) { + while (typeof x === "string") { + x; // string + x = undefined; + } + x; // number +} +function b(x: string | number) { + while (typeof x === "string") { + if (cond) continue; + x; // string + x = undefined; + } + x; // number +} +function c(x: string | number) { + while (typeof x === "string") { + if (cond) break; + x; // string + x = undefined; + } + x; // string | number +} From 9e965d408c840c87ab54511ebab6f22c57a75427 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 25 Mar 2016 17:03:00 -0700 Subject: [PATCH 10/67] Fix issues in analysis of do..while and for..in/for..of --- src/compiler/binder.ts | 13 +++++++++---- src/compiler/checker.ts | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b49e22e8192f5..cfeb76827bfc0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -636,6 +636,9 @@ namespace ts { } function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode { + if (antecedent.kind === FlowKind.Unreachable) { + return antecedent; + } if (!expression) { return assumeTrue ? antecedent : unreachableFlow; } @@ -696,16 +699,19 @@ namespace ts { function bindDoStatement(node: DoStatement): void { const preDoLabel = createFlowLabel(); + const preConditionLabel = createFlowLabel(); const postDoLabel = createFlowLabel(); addAntecedent(preDoLabel, currentFlow); currentFlow = preDoLabel; const saveBreakTarget = breakTarget; const saveContinueTarget = continueTarget; breakTarget = postDoLabel; - continueTarget = preDoLabel; + continueTarget = preConditionLabel; bind(node.statement); breakTarget = saveBreakTarget; continueTarget = saveContinueTarget; + addAntecedent(preConditionLabel, currentFlow); + currentFlow = finishFlow(preConditionLabel); bind(node.expression); addAntecedent(preDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true)); addAntecedent(postDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ false)); @@ -737,10 +743,10 @@ namespace ts { const preLoopLabel = createFlowLabel(); const postLoopLabel = createFlowLabel(); bind(node.initializer); - bind(node.expression); addAntecedent(preLoopLabel, currentFlow); - addAntecedent(postLoopLabel, currentFlow); currentFlow = preLoopLabel; + bind(node.expression); + addAntecedent(postLoopLabel, currentFlow); const saveBreakTarget = breakTarget; const saveContinueTarget = continueTarget; breakTarget = postLoopLabel; @@ -750,7 +756,6 @@ namespace ts { breakTarget = saveBreakTarget; continueTarget = saveContinueTarget; addAntecedent(preLoopLabel, currentFlow); - addAntecedent(postLoopLabel, currentFlow); currentFlow = finishFlow(postLoopLabel); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ae6faeff74c2..d8c62dd2da3f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8674,7 +8674,9 @@ namespace ts { const parent = node.parent; return parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && - (parent).left === node; + (parent).left === node || + (parent.kind === SyntaxKind.ForInStatement || parent.kind === SyntaxKind.ForOfStatement) && + (parent).initializer === node; } function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type { From 9de0a5d8331f3fd3266d85078ac72c2664efafcc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 25 Mar 2016 17:03:12 -0700 Subject: [PATCH 11/67] Fix comment in test --- .../cases/conformance/controlFlow/controlFlowForOfStatement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts index ac4c584e1dcae..3abcf814f2a19 100644 --- a/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts @@ -6,5 +6,5 @@ function a() { for (x of obj) { x = x.toExponential(); } - x; // number | boolean + x; // string | boolean } From 560bc3f38c845090b76eb08b8259551db8f36d95 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 25 Mar 2016 17:09:09 -0700 Subject: [PATCH 12/67] Accepting new baselines --- .../reference/TypeGuardWithEnumUnion.types | 10 +- .../controlFlowAssignmentExpression.js | 22 ++ .../controlFlowAssignmentExpression.symbols | 33 +++ .../controlFlowAssignmentExpression.types | 47 ++++ .../controlFlowBinaryAndExpression.js | 20 ++ .../controlFlowBinaryAndExpression.symbols | 24 ++ .../controlFlowBinaryAndExpression.types | 37 +++ .../controlFlowBinaryOrExpression.js | 20 ++ .../controlFlowBinaryOrExpression.symbols | 24 ++ .../controlFlowBinaryOrExpression.types | 37 +++ .../controlFlowConditionalExpression.js | 13 ++ .../controlFlowConditionalExpression.symbols | 15 ++ .../controlFlowConditionalExpression.types | 20 ++ .../reference/controlFlowDoWhileStatement.js | 157 +++++++++++++ .../controlFlowDoWhileStatement.symbols | 181 ++++++++++++++ .../controlFlowDoWhileStatement.types | 220 ++++++++++++++++++ .../reference/controlFlowForInStatement.js | 37 +++ .../controlFlowForInStatement.symbols | 43 ++++ .../reference/controlFlowForInStatement.types | 50 ++++ .../reference/controlFlowForOfStatement.js | 24 ++ .../controlFlowForOfStatement.symbols | 28 +++ .../reference/controlFlowForOfStatement.types | 32 +++ .../reference/controlFlowForStatement.js | 87 +++++++ .../reference/controlFlowForStatement.symbols | 112 +++++++++ .../reference/controlFlowForStatement.types | 152 ++++++++++++ .../reference/controlFlowIfStatement.js | 74 ++++++ .../reference/controlFlowIfStatement.symbols | 74 ++++++ .../reference/controlFlowIfStatement.types | 93 ++++++++ .../reference/controlFlowWhileStatement.js | 155 ++++++++++++ .../controlFlowWhileStatement.symbols | 177 ++++++++++++++ .../reference/controlFlowWhileStatement.types | 216 +++++++++++++++++ tests/baselines/reference/for-of45.types | 2 +- ...structuringForOfArrayBindingPattern2.types | 48 ++-- ...rOfArrayBindingPatternDefaultValues2.types | 36 +-- ...OfObjectBindingPatternDefaultValues2.types | 48 ++-- .../baselines/reference/systemModule13.types | 2 +- tests/baselines/reference/systemModule8.types | 2 +- 37 files changed, 2298 insertions(+), 74 deletions(-) create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.js create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.symbols create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.types create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.js create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.symbols create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.types create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.js create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.symbols create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.types create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.js create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.symbols create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.types create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.js create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.symbols create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.types create mode 100644 tests/baselines/reference/controlFlowForInStatement.js create mode 100644 tests/baselines/reference/controlFlowForInStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForInStatement.types create mode 100644 tests/baselines/reference/controlFlowForOfStatement.js create mode 100644 tests/baselines/reference/controlFlowForOfStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForOfStatement.types create mode 100644 tests/baselines/reference/controlFlowForStatement.js create mode 100644 tests/baselines/reference/controlFlowForStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForStatement.types create mode 100644 tests/baselines/reference/controlFlowIfStatement.js create mode 100644 tests/baselines/reference/controlFlowIfStatement.symbols create mode 100644 tests/baselines/reference/controlFlowIfStatement.types create mode 100644 tests/baselines/reference/controlFlowWhileStatement.js create mode 100644 tests/baselines/reference/controlFlowWhileStatement.symbols create mode 100644 tests/baselines/reference/controlFlowWhileStatement.types diff --git a/tests/baselines/reference/TypeGuardWithEnumUnion.types b/tests/baselines/reference/TypeGuardWithEnumUnion.types index 453ec220f021a..05d55cb0dbba1 100644 --- a/tests/baselines/reference/TypeGuardWithEnumUnion.types +++ b/tests/baselines/reference/TypeGuardWithEnumUnion.types @@ -55,7 +55,7 @@ function f2(x: Color | string | string[]) { if (typeof x === "number") { >typeof x === "number" : boolean >typeof x : string ->x : Color | string | string[] +>x : string[] | Color | string >"number" : string var z = x; @@ -68,16 +68,16 @@ function f2(x: Color | string | string[]) { } else { var w = x; ->w : string | string[] ->x : string | string[] +>w : string[] | string +>x : string[] | string var w: string | string[]; ->w : string | string[] +>w : string[] | string } if (typeof x === "string") { >typeof x === "string" : boolean >typeof x : string ->x : Color | string | string[] +>x : Color | string[] | string >"string" : string var a = x; diff --git a/tests/baselines/reference/controlFlowAssignmentExpression.js b/tests/baselines/reference/controlFlowAssignmentExpression.js new file mode 100644 index 0000000000000..cf8d70b92deb0 --- /dev/null +++ b/tests/baselines/reference/controlFlowAssignmentExpression.js @@ -0,0 +1,22 @@ +//// [controlFlowAssignmentExpression.ts] +let x: string | boolean | number; +let obj: any; + +x = ""; +x = x.length; +x; // number + +x = true; +(x = "", obj).foo = (x = x.length); +x; // number + + +//// [controlFlowAssignmentExpression.js] +var x; +var obj; +x = ""; +x = x.length; +x; // number +x = true; +(x = "", obj).foo = (x = x.length); +x; // number diff --git a/tests/baselines/reference/controlFlowAssignmentExpression.symbols b/tests/baselines/reference/controlFlowAssignmentExpression.symbols new file mode 100644 index 0000000000000..470e3114ca8e7 --- /dev/null +++ b/tests/baselines/reference/controlFlowAssignmentExpression.symbols @@ -0,0 +1,33 @@ +=== tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts === +let x: string | boolean | number; +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) + +let obj: any; +>obj : Symbol(obj, Decl(controlFlowAssignmentExpression.ts, 1, 3)) + +x = ""; +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) + +x = x.length; +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + +x; // number +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) + +x = true; +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) + +(x = "", obj).foo = (x = x.length); +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) +>obj : Symbol(obj, Decl(controlFlowAssignmentExpression.ts, 1, 3)) +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + +x; // number +>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) + diff --git a/tests/baselines/reference/controlFlowAssignmentExpression.types b/tests/baselines/reference/controlFlowAssignmentExpression.types new file mode 100644 index 0000000000000..24355fd8c4a40 --- /dev/null +++ b/tests/baselines/reference/controlFlowAssignmentExpression.types @@ -0,0 +1,47 @@ +=== tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts === +let x: string | boolean | number; +>x : string | boolean | number + +let obj: any; +>obj : any + +x = ""; +>x = "" : string +>x : string | boolean | number +>"" : string + +x = x.length; +>x = x.length : number +>x : string | boolean | number +>x.length : number +>x : string +>length : number + +x; // number +>x : number + +x = true; +>x = true : boolean +>x : string | boolean | number +>true : boolean + +(x = "", obj).foo = (x = x.length); +>(x = "", obj).foo = (x = x.length) : number +>(x = "", obj).foo : any +>(x = "", obj) : any +>x = "", obj : any +>x = "" : string +>x : string | boolean | number +>"" : string +>obj : any +>foo : any +>(x = x.length) : number +>x = x.length : number +>x : string | boolean | number +>x.length : number +>x : string +>length : number + +x; // number +>x : number + diff --git a/tests/baselines/reference/controlFlowBinaryAndExpression.js b/tests/baselines/reference/controlFlowBinaryAndExpression.js new file mode 100644 index 0000000000000..eb89d1a78cfb1 --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryAndExpression.js @@ -0,0 +1,20 @@ +//// [controlFlowBinaryAndExpression.ts] +let x: string | number | boolean; +let cond: boolean; + +(x = "") && (x = 0); +x; // string | number + +x = ""; +cond && (x = 0); +x; // string | number + + +//// [controlFlowBinaryAndExpression.js] +var x; +var cond; +(x = "") && (x = 0); +x; // string | number +x = ""; +cond && (x = 0); +x; // string | number diff --git a/tests/baselines/reference/controlFlowBinaryAndExpression.symbols b/tests/baselines/reference/controlFlowBinaryAndExpression.symbols new file mode 100644 index 0000000000000..5be553f680234 --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryAndExpression.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts === +let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowBinaryAndExpression.ts, 1, 3)) + +(x = "") && (x = 0); +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + +x = ""; +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + +cond && (x = 0); +>cond : Symbol(cond, Decl(controlFlowBinaryAndExpression.ts, 1, 3)) +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(controlFlowBinaryAndExpression.ts, 0, 3)) + diff --git a/tests/baselines/reference/controlFlowBinaryAndExpression.types b/tests/baselines/reference/controlFlowBinaryAndExpression.types new file mode 100644 index 0000000000000..8a1924c42eb3b --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryAndExpression.types @@ -0,0 +1,37 @@ +=== tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts === +let x: string | number | boolean; +>x : string | number | boolean + +let cond: boolean; +>cond : boolean + +(x = "") && (x = 0); +>(x = "") && (x = 0) : number +>(x = "") : string +>x = "" : string +>x : string | number | boolean +>"" : string +>(x = 0) : number +>x = 0 : number +>x : string | number | boolean +>0 : number + +x; // string | number +>x : string | number + +x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + +cond && (x = 0); +>cond && (x = 0) : number +>cond : boolean +>(x = 0) : number +>x = 0 : number +>x : string | number | boolean +>0 : number + +x; // string | number +>x : string | number + diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.js b/tests/baselines/reference/controlFlowBinaryOrExpression.js new file mode 100644 index 0000000000000..350e383a1f8e6 --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.js @@ -0,0 +1,20 @@ +//// [controlFlowBinaryOrExpression.ts] +let x: string | number | boolean; +let cond: boolean; + +(x = "") || (x = 0); +x; // string | number + +x = ""; +cond || (x = 0); +x; // string | number + + +//// [controlFlowBinaryOrExpression.js] +var x; +var cond; +(x = "") || (x = 0); +x; // string | number +x = ""; +cond || (x = 0); +x; // string | number diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols new file mode 100644 index 0000000000000..286612ef4df9b --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts === +let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowBinaryOrExpression.ts, 1, 3)) + +(x = "") || (x = 0); +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + +x = ""; +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + +cond || (x = 0); +>cond : Symbol(cond, Decl(controlFlowBinaryOrExpression.ts, 1, 3)) +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) + diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types new file mode 100644 index 0000000000000..404e153788afe --- /dev/null +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -0,0 +1,37 @@ +=== tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts === +let x: string | number | boolean; +>x : string | number | boolean + +let cond: boolean; +>cond : boolean + +(x = "") || (x = 0); +>(x = "") || (x = 0) : string | number +>(x = "") : string +>x = "" : string +>x : string | number | boolean +>"" : string +>(x = 0) : number +>x = 0 : number +>x : string | number | boolean +>0 : number + +x; // string | number +>x : string | number + +x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + +cond || (x = 0); +>cond || (x = 0) : boolean | number +>cond : boolean +>(x = 0) : number +>x = 0 : number +>x : string | number | boolean +>0 : number + +x; // string | number +>x : string | number + diff --git a/tests/baselines/reference/controlFlowConditionalExpression.js b/tests/baselines/reference/controlFlowConditionalExpression.js new file mode 100644 index 0000000000000..f39b999039b58 --- /dev/null +++ b/tests/baselines/reference/controlFlowConditionalExpression.js @@ -0,0 +1,13 @@ +//// [controlFlowConditionalExpression.ts] +let x: string | number | boolean; +let cond: boolean; + +cond ? x = "" : x = 3; +x; // string | number + + +//// [controlFlowConditionalExpression.js] +var x; +var cond; +cond ? x = "" : x = 3; +x; // string | number diff --git a/tests/baselines/reference/controlFlowConditionalExpression.symbols b/tests/baselines/reference/controlFlowConditionalExpression.symbols new file mode 100644 index 0000000000000..b1a4074d08d6b --- /dev/null +++ b/tests/baselines/reference/controlFlowConditionalExpression.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts === +let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowConditionalExpression.ts, 0, 3)) + +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowConditionalExpression.ts, 1, 3)) + +cond ? x = "" : x = 3; +>cond : Symbol(cond, Decl(controlFlowConditionalExpression.ts, 1, 3)) +>x : Symbol(x, Decl(controlFlowConditionalExpression.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowConditionalExpression.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(controlFlowConditionalExpression.ts, 0, 3)) + diff --git a/tests/baselines/reference/controlFlowConditionalExpression.types b/tests/baselines/reference/controlFlowConditionalExpression.types new file mode 100644 index 0000000000000..c6084e052b172 --- /dev/null +++ b/tests/baselines/reference/controlFlowConditionalExpression.types @@ -0,0 +1,20 @@ +=== tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts === +let x: string | number | boolean; +>x : string | number | boolean + +let cond: boolean; +>cond : boolean + +cond ? x = "" : x = 3; +>cond ? x = "" : x = 3 : string | number +>cond : boolean +>x = "" : string +>x : string | number | boolean +>"" : string +>x = 3 : number +>x : string | number | boolean +>3 : number + +x; // string | number +>x : string | number + diff --git a/tests/baselines/reference/controlFlowDoWhileStatement.js b/tests/baselines/reference/controlFlowDoWhileStatement.js new file mode 100644 index 0000000000000..635a5a59257d5 --- /dev/null +++ b/tests/baselines/reference/controlFlowDoWhileStatement.js @@ -0,0 +1,157 @@ +//// [controlFlowDoWhileStatement.ts] +let cond: boolean; +function a() { + let x: string | number; + x = ""; + do { + x; // string + } while (cond) +} +function b() { + let x: string | number; + x = ""; + do { + x; // string + x = 42; + break; + } while (cond) +} +function c() { + let x: string | number; + x = ""; + do { + x; // string + x = undefined; + if (typeof x === "string") continue; + break; + } while (cond) +} +function d() { + let x: string | number; + x = 1000; + do { + x; // number + x = ""; + } while (x = x.length) + x; // number +} +function e() { + let x: string | number; + x = ""; + do { + x = 42; + } while (cond) + x; // number +} +function f() { + let x: string | number | boolean | RegExp | Function; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (cond) + x; // number | boolean | RegExp +} +function g() { + let x: string | number | boolean | RegExp | Function; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (true) + x; // number +} + + +//// [controlFlowDoWhileStatement.js] +var cond; +function a() { + var x; + x = ""; + do { + x; // string + } while (cond); +} +function b() { + var x; + x = ""; + do { + x; // string + x = 42; + break; + } while (cond); +} +function c() { + var x; + x = ""; + do { + x; // string + x = undefined; + if (typeof x === "string") + continue; + break; + } while (cond); +} +function d() { + var x; + x = 1000; + do { + x; // number + x = ""; + } while (x = x.length); + x; // number +} +function e() { + var x; + x = ""; + do { + x = 42; + } while (cond); + x; // number +} +function f() { + var x; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (cond); + x; // number | boolean | RegExp +} +function g() { + var x; + x = ""; + do { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } while (true); + x; // number +} diff --git a/tests/baselines/reference/controlFlowDoWhileStatement.symbols b/tests/baselines/reference/controlFlowDoWhileStatement.symbols new file mode 100644 index 0000000000000..2a3f3858ec22a --- /dev/null +++ b/tests/baselines/reference/controlFlowDoWhileStatement.symbols @@ -0,0 +1,181 @@ +=== tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + +function a() { +>a : Symbol(a, Decl(controlFlowDoWhileStatement.ts, 0, 18)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 2, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 2, 7)) + + do { + x; // string +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 2, 7)) + + } while (cond) +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) +} +function b() { +>b : Symbol(b, Decl(controlFlowDoWhileStatement.ts, 7, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 9, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 9, 7)) + + do { + x; // string +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 9, 7)) + + x = 42; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 9, 7)) + + break; + } while (cond) +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) +} +function c() { +>c : Symbol(c, Decl(controlFlowDoWhileStatement.ts, 16, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 18, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 18, 7)) + + do { + x; // string +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 18, 7)) + + x = undefined; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 18, 7)) +>undefined : Symbol(undefined) + + if (typeof x === "string") continue; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 18, 7)) + + break; + } while (cond) +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) +} +function d() { +>d : Symbol(d, Decl(controlFlowDoWhileStatement.ts, 26, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) + + x = 1000; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) + + do { + x; // number +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) + + } while (x = x.length) +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + + x; // number +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 28, 7)) +} +function e() { +>e : Symbol(e, Decl(controlFlowDoWhileStatement.ts, 35, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 37, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 37, 7)) + + do { + x = 42; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 37, 7)) + + } while (cond) +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x; // number +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 37, 7)) +} +function f() { +>f : Symbol(f, Decl(controlFlowDoWhileStatement.ts, 43, 1)) + + let x: string | number | boolean | RegExp | Function; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) + + do { + if (cond) { +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) + + break; + } + if (cond) { +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x = true; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) + + continue; + } + x = /a/; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) + + } while (cond) +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x; // number | boolean | RegExp +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 45, 7)) +} +function g() { +>g : Symbol(g, Decl(controlFlowDoWhileStatement.ts, 59, 1)) + + let x: string | number | boolean | RegExp | Function; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x = ""; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) + + do { + if (cond) { +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) + + break; + } + if (cond) { +>cond : Symbol(cond, Decl(controlFlowDoWhileStatement.ts, 0, 3)) + + x = true; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) + + continue; + } + x = /a/; +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) + + } while (true) + x; // number +>x : Symbol(x, Decl(controlFlowDoWhileStatement.ts, 61, 7)) +} + diff --git a/tests/baselines/reference/controlFlowDoWhileStatement.types b/tests/baselines/reference/controlFlowDoWhileStatement.types new file mode 100644 index 0000000000000..a82ae1b716efe --- /dev/null +++ b/tests/baselines/reference/controlFlowDoWhileStatement.types @@ -0,0 +1,220 @@ +=== tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts === +let cond: boolean; +>cond : boolean + +function a() { +>a : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + do { + x; // string +>x : string + + } while (cond) +>cond : boolean +} +function b() { +>b : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + do { + x; // string +>x : string + + x = 42; +>x = 42 : number +>x : string | number +>42 : number + + break; + } while (cond) +>cond : boolean +} +function c() { +>c : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + do { + x; // string +>x : string + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + + if (typeof x === "string") continue; +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + break; + } while (cond) +>cond : boolean +} +function d() { +>d : () => void + + let x: string | number; +>x : string | number + + x = 1000; +>x = 1000 : number +>x : string | number +>1000 : number + + do { + x; // number +>x : number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + } while (x = x.length) +>x = x.length : number +>x : string | number +>x.length : number +>x : string +>length : number + + x; // number +>x : number +} +function e() { +>e : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + do { + x = 42; +>x = 42 : number +>x : string | number +>42 : number + + } while (cond) +>cond : boolean + + x; // number +>x : number +} +function f() { +>f : () => void + + let x: string | number | boolean | RegExp | Function; +>x : string | number | boolean | RegExp | Function +>RegExp : RegExp +>Function : Function + + x = ""; +>x = "" : string +>x : string | number | boolean | RegExp | Function +>"" : string + + do { + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp | Function +>42 : number + + break; + } + if (cond) { +>cond : boolean + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp | Function +>true : boolean + + continue; + } + x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp | Function +>/a/ : RegExp + + } while (cond) +>cond : boolean + + x; // number | boolean | RegExp +>x : number | boolean | RegExp +} +function g() { +>g : () => void + + let x: string | number | boolean | RegExp | Function; +>x : string | number | boolean | RegExp | Function +>RegExp : RegExp +>Function : Function + + x = ""; +>x = "" : string +>x : string | number | boolean | RegExp | Function +>"" : string + + do { + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp | Function +>42 : number + + break; + } + if (cond) { +>cond : boolean + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp | Function +>true : boolean + + continue; + } + x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp | Function +>/a/ : RegExp + + } while (true) +>true : boolean + + x; // number +>x : number +} + diff --git a/tests/baselines/reference/controlFlowForInStatement.js b/tests/baselines/reference/controlFlowForInStatement.js new file mode 100644 index 0000000000000..1d3f29de2ad60 --- /dev/null +++ b/tests/baselines/reference/controlFlowForInStatement.js @@ -0,0 +1,37 @@ +//// [controlFlowForInStatement.ts] +let x: string | number | boolean | RegExp | Function; +let obj: any; +let cond: boolean; + +x = /a/; +for (let y in obj) { + x = y; + if (cond) { + x = 42; + continue; + } + if (cond) { + x = true; + break; + } +} +x; // RegExp | string | number | boolean + + +//// [controlFlowForInStatement.js] +var x; +var obj; +var cond; +x = /a/; +for (var y in obj) { + x = y; + if (cond) { + x = 42; + continue; + } + if (cond) { + x = true; + break; + } +} +x; // RegExp | string | number | boolean diff --git a/tests/baselines/reference/controlFlowForInStatement.symbols b/tests/baselines/reference/controlFlowForInStatement.symbols new file mode 100644 index 0000000000000..8d01ff5019312 --- /dev/null +++ b/tests/baselines/reference/controlFlowForInStatement.symbols @@ -0,0 +1,43 @@ +=== tests/cases/conformance/controlFlow/controlFlowForInStatement.ts === +let x: string | number | boolean | RegExp | Function; +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +let obj: any; +>obj : Symbol(obj, Decl(controlFlowForInStatement.ts, 1, 3)) + +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowForInStatement.ts, 2, 3)) + +x = /a/; +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) + +for (let y in obj) { +>y : Symbol(y, Decl(controlFlowForInStatement.ts, 5, 8)) +>obj : Symbol(obj, Decl(controlFlowForInStatement.ts, 1, 3)) + + x = y; +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) +>y : Symbol(y, Decl(controlFlowForInStatement.ts, 5, 8)) + + if (cond) { +>cond : Symbol(cond, Decl(controlFlowForInStatement.ts, 2, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) + + continue; + } + if (cond) { +>cond : Symbol(cond, Decl(controlFlowForInStatement.ts, 2, 3)) + + x = true; +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) + + break; + } +} +x; // RegExp | string | number | boolean +>x : Symbol(x, Decl(controlFlowForInStatement.ts, 0, 3)) + diff --git a/tests/baselines/reference/controlFlowForInStatement.types b/tests/baselines/reference/controlFlowForInStatement.types new file mode 100644 index 0000000000000..e46db37bb16fb --- /dev/null +++ b/tests/baselines/reference/controlFlowForInStatement.types @@ -0,0 +1,50 @@ +=== tests/cases/conformance/controlFlow/controlFlowForInStatement.ts === +let x: string | number | boolean | RegExp | Function; +>x : string | number | boolean | RegExp | Function +>RegExp : RegExp +>Function : Function + +let obj: any; +>obj : any + +let cond: boolean; +>cond : boolean + +x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp | Function +>/a/ : RegExp + +for (let y in obj) { +>y : string +>obj : any + + x = y; +>x = y : string +>x : string | number | boolean | RegExp | Function +>y : string + + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp | Function +>42 : number + + continue; + } + if (cond) { +>cond : boolean + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp | Function +>true : boolean + + break; + } +} +x; // RegExp | string | number | boolean +>x : RegExp | number | string | boolean + diff --git a/tests/baselines/reference/controlFlowForOfStatement.js b/tests/baselines/reference/controlFlowForOfStatement.js new file mode 100644 index 0000000000000..31ba7f1a34621 --- /dev/null +++ b/tests/baselines/reference/controlFlowForOfStatement.js @@ -0,0 +1,24 @@ +//// [controlFlowForOfStatement.ts] +let obj: number[]; +let x: string | number | boolean | RegExp; + +function a() { + x = true; + for (x of obj) { + x = x.toExponential(); + } + x; // string | boolean +} + + +//// [controlFlowForOfStatement.js] +var obj; +var x; +function a() { + x = true; + for (var _i = 0, obj_1 = obj; _i < obj_1.length; _i++) { + x = obj_1[_i]; + x = x.toExponential(); + } + x; // string | boolean +} diff --git a/tests/baselines/reference/controlFlowForOfStatement.symbols b/tests/baselines/reference/controlFlowForOfStatement.symbols new file mode 100644 index 0000000000000..0f1e754be41f5 --- /dev/null +++ b/tests/baselines/reference/controlFlowForOfStatement.symbols @@ -0,0 +1,28 @@ +=== tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts === +let obj: number[]; +>obj : Symbol(obj, Decl(controlFlowForOfStatement.ts, 0, 3)) + +let x: string | number | boolean | RegExp; +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +function a() { +>a : Symbol(a, Decl(controlFlowForOfStatement.ts, 1, 42)) + + x = true; +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) + + for (x of obj) { +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) +>obj : Symbol(obj, Decl(controlFlowForOfStatement.ts, 0, 3)) + + x = x.toExponential(); +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) +>x.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) +>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) + } + x; // string | boolean +>x : Symbol(x, Decl(controlFlowForOfStatement.ts, 1, 3)) +} + diff --git a/tests/baselines/reference/controlFlowForOfStatement.types b/tests/baselines/reference/controlFlowForOfStatement.types new file mode 100644 index 0000000000000..437576e51b6c4 --- /dev/null +++ b/tests/baselines/reference/controlFlowForOfStatement.types @@ -0,0 +1,32 @@ +=== tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts === +let obj: number[]; +>obj : number[] + +let x: string | number | boolean | RegExp; +>x : string | number | boolean | RegExp +>RegExp : RegExp + +function a() { +>a : () => void + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp +>true : boolean + + for (x of obj) { +>x : string | number | boolean | RegExp +>obj : number[] + + x = x.toExponential(); +>x = x.toExponential() : string +>x : string | number | boolean | RegExp +>x.toExponential() : string +>x.toExponential : (fractionDigits?: number) => string +>x : number +>toExponential : (fractionDigits?: number) => string + } + x; // string | boolean +>x : boolean | string +} + diff --git a/tests/baselines/reference/controlFlowForStatement.js b/tests/baselines/reference/controlFlowForStatement.js new file mode 100644 index 0000000000000..d9b1d0814fef0 --- /dev/null +++ b/tests/baselines/reference/controlFlowForStatement.js @@ -0,0 +1,87 @@ +//// [controlFlowForStatement.ts] +let cond: boolean; +function a() { + let x: string | number | boolean; + for (x = ""; cond; x = 5) { + x; // string | number + } +} +function b() { + let x: string | number | boolean; + for (x = 5; cond; x = x.length) { + x; // number + x = ""; + } +} +function c() { + let x: string | number | boolean; + for (x = 5; x = x.toExponential(); x = 5) { + x; // string + } +} +function d() { + let x: string | number | boolean; + for (x = ""; typeof x === "string"; x = 5) { + x; // string + } +} +function e() { + let x: string | number | boolean | RegExp; + for (x = "" || 0; typeof x !== "string"; x = "" || true) { + x; // number | boolean + } +} +function f() { + let x: string | number | boolean; + for (; typeof x !== "string";) { + x; // number | boolean + if (typeof x === "number") break; + x = undefined; + } + x; // string | number +} + + +//// [controlFlowForStatement.js] +var cond; +function a() { + var x; + for (x = ""; cond; x = 5) { + x; // string | number + } +} +function b() { + var x; + for (x = 5; cond; x = x.length) { + x; // number + x = ""; + } +} +function c() { + var x; + for (x = 5; x = x.toExponential(); x = 5) { + x; // string + } +} +function d() { + var x; + for (x = ""; typeof x === "string"; x = 5) { + x; // string + } +} +function e() { + var x; + for (x = "" || 0; typeof x !== "string"; x = "" || true) { + x; // number | boolean + } +} +function f() { + var x; + for (; typeof x !== "string";) { + x; // number | boolean + if (typeof x === "number") + break; + x = undefined; + } + x; // string | number +} diff --git a/tests/baselines/reference/controlFlowForStatement.symbols b/tests/baselines/reference/controlFlowForStatement.symbols new file mode 100644 index 0000000000000..1918fe0ca33a3 --- /dev/null +++ b/tests/baselines/reference/controlFlowForStatement.symbols @@ -0,0 +1,112 @@ +=== tests/cases/conformance/controlFlow/controlFlowForStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowForStatement.ts, 0, 3)) + +function a() { +>a : Symbol(a, Decl(controlFlowForStatement.ts, 0, 18)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 2, 7)) + + for (x = ""; cond; x = 5) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 2, 7)) +>cond : Symbol(cond, Decl(controlFlowForStatement.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 2, 7)) + + x; // string | number +>x : Symbol(x, Decl(controlFlowForStatement.ts, 2, 7)) + } +} +function b() { +>b : Symbol(b, Decl(controlFlowForStatement.ts, 6, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) + + for (x = 5; cond; x = x.length) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) +>cond : Symbol(cond, Decl(controlFlowForStatement.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + + x; // number +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 8, 7)) + } +} +function c() { +>c : Symbol(c, Decl(controlFlowForStatement.ts, 13, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) + + for (x = 5; x = x.toExponential(); x = 5) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) +>x.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) + + x; // string +>x : Symbol(x, Decl(controlFlowForStatement.ts, 15, 7)) + } +} +function d() { +>d : Symbol(d, Decl(controlFlowForStatement.ts, 19, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 21, 7)) + + for (x = ""; typeof x === "string"; x = 5) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 21, 7)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 21, 7)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 21, 7)) + + x; // string +>x : Symbol(x, Decl(controlFlowForStatement.ts, 21, 7)) + } +} +function e() { +>e : Symbol(e, Decl(controlFlowForStatement.ts, 25, 1)) + + let x: string | number | boolean | RegExp; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 27, 7)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + for (x = "" || 0; typeof x !== "string"; x = "" || true) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 27, 7)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 27, 7)) +>x : Symbol(x, Decl(controlFlowForStatement.ts, 27, 7)) + + x; // number | boolean +>x : Symbol(x, Decl(controlFlowForStatement.ts, 27, 7)) + } +} +function f() { +>f : Symbol(f, Decl(controlFlowForStatement.ts, 31, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) + + for (; typeof x !== "string";) { +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) + + x; // number | boolean +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) + + if (typeof x === "number") break; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) + + x = undefined; +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) +>undefined : Symbol(undefined) + } + x; // string | number +>x : Symbol(x, Decl(controlFlowForStatement.ts, 33, 7)) +} + diff --git a/tests/baselines/reference/controlFlowForStatement.types b/tests/baselines/reference/controlFlowForStatement.types new file mode 100644 index 0000000000000..cdf049b32d6ab --- /dev/null +++ b/tests/baselines/reference/controlFlowForStatement.types @@ -0,0 +1,152 @@ +=== tests/cases/conformance/controlFlow/controlFlowForStatement.ts === +let cond: boolean; +>cond : boolean + +function a() { +>a : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + for (x = ""; cond; x = 5) { +>x = "" : string +>x : string | number | boolean +>"" : string +>cond : boolean +>x = 5 : number +>x : string | number | boolean +>5 : number + + x; // string | number +>x : string | number + } +} +function b() { +>b : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + for (x = 5; cond; x = x.length) { +>x = 5 : number +>x : string | number | boolean +>5 : number +>cond : boolean +>x = x.length : number +>x : string | number | boolean +>x.length : number +>x : string +>length : number + + x; // number +>x : number + + x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + } +} +function c() { +>c : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + for (x = 5; x = x.toExponential(); x = 5) { +>x = 5 : number +>x : string | number | boolean +>5 : number +>x = x.toExponential() : string +>x : string | number | boolean +>x.toExponential() : string +>x.toExponential : (fractionDigits?: number) => string +>x : number +>toExponential : (fractionDigits?: number) => string +>x = 5 : number +>x : string | number | boolean +>5 : number + + x; // string +>x : string + } +} +function d() { +>d : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + for (x = ""; typeof x === "string"; x = 5) { +>x = "" : string +>x : string | number | boolean +>"" : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string +>x = 5 : number +>x : string | number | boolean +>5 : number + + x; // string +>x : string + } +} +function e() { +>e : () => void + + let x: string | number | boolean | RegExp; +>x : string | number | boolean | RegExp +>RegExp : RegExp + + for (x = "" || 0; typeof x !== "string"; x = "" || true) { +>x = "" || 0 : string | number +>x : string | number | boolean | RegExp +>"" || 0 : string | number +>"" : string +>0 : number +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean +>"string" : string +>x = "" || true : string | boolean +>x : string | number | boolean | RegExp +>"" || true : string | boolean +>"" : string +>true : boolean + + x; // number | boolean +>x : number | boolean + } +} +function f() { +>f : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + for (; typeof x !== "string";) { +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number | boolean +>"string" : string + + x; // number | boolean +>x : number | boolean + + if (typeof x === "number") break; +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean +>"number" : string + + x = undefined; +>x = undefined : undefined +>x : string | number | boolean +>undefined : undefined + } + x; // string | number +>x : string | number +} + diff --git a/tests/baselines/reference/controlFlowIfStatement.js b/tests/baselines/reference/controlFlowIfStatement.js new file mode 100644 index 0000000000000..a70c07d38dc1b --- /dev/null +++ b/tests/baselines/reference/controlFlowIfStatement.js @@ -0,0 +1,74 @@ +//// [controlFlowIfStatement.ts] +let x: string | number | boolean | RegExp; +let cond: boolean; + +x = /a/; +if (x /* RegExp */, (x = true)) { + x; // boolean + x = ""; +} +else { + x; // boolean + x = 42; +} +x; // string | number + +function a() { + let x: string | number; + if (cond) { + x = 42; + } + else { + x = ""; + return; + } + x; // number +} +function b() { + let x: string | number; + if (cond) { + x = 42; + throw ""; + } + else { + x = ""; + } + x; // string +} + + +//// [controlFlowIfStatement.js] +var x; +var cond; +x = /a/; +if (x /* RegExp */, (x = true)) { + x; // boolean + x = ""; +} +else { + x; // boolean + x = 42; +} +x; // string | number +function a() { + var x; + if (cond) { + x = 42; + } + else { + x = ""; + return; + } + x; // number +} +function b() { + var x; + if (cond) { + x = 42; + throw ""; + } + else { + x = ""; + } + x; // string +} diff --git a/tests/baselines/reference/controlFlowIfStatement.symbols b/tests/baselines/reference/controlFlowIfStatement.symbols new file mode 100644 index 0000000000000..1d85b31c9988f --- /dev/null +++ b/tests/baselines/reference/controlFlowIfStatement.symbols @@ -0,0 +1,74 @@ +=== tests/cases/conformance/controlFlow/controlFlowIfStatement.ts === +let x: string | number | boolean | RegExp; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowIfStatement.ts, 1, 3)) + +x = /a/; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) + +if (x /* RegExp */, (x = true)) { +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) + + x; // boolean +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) + + x = ""; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) +} +else { + x; // boolean +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) +} +x; // string | number +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 0, 3)) + +function a() { +>a : Symbol(a, Decl(controlFlowIfStatement.ts, 12, 2)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 15, 7)) + + if (cond) { +>cond : Symbol(cond, Decl(controlFlowIfStatement.ts, 1, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 15, 7)) + } + else { + x = ""; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 15, 7)) + + return; + } + x; // number +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 15, 7)) +} +function b() { +>b : Symbol(b, Decl(controlFlowIfStatement.ts, 24, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) + + if (cond) { +>cond : Symbol(cond, Decl(controlFlowIfStatement.ts, 1, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) + + throw ""; + } + else { + x = ""; +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) + } + x; // string +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) +} + diff --git a/tests/baselines/reference/controlFlowIfStatement.types b/tests/baselines/reference/controlFlowIfStatement.types new file mode 100644 index 0000000000000..dc07e23275f0f --- /dev/null +++ b/tests/baselines/reference/controlFlowIfStatement.types @@ -0,0 +1,93 @@ +=== tests/cases/conformance/controlFlow/controlFlowIfStatement.ts === +let x: string | number | boolean | RegExp; +>x : string | number | boolean | RegExp +>RegExp : RegExp + +let cond: boolean; +>cond : boolean + +x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp +>/a/ : RegExp + +if (x /* RegExp */, (x = true)) { +>x /* RegExp */, (x = true) : boolean +>x : RegExp +>(x = true) : boolean +>x = true : boolean +>x : string | number | boolean | RegExp +>true : boolean + + x; // boolean +>x : boolean + + x = ""; +>x = "" : string +>x : string | number | boolean | RegExp +>"" : string +} +else { + x; // boolean +>x : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp +>42 : number +} +x; // string | number +>x : string | number + +function a() { +>a : () => void + + let x: string | number; +>x : string | number + + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number +>42 : number + } + else { + x = ""; +>x = "" : string +>x : string | number +>"" : string + + return; + } + x; // number +>x : number +} +function b() { +>b : () => void + + let x: string | number; +>x : string | number + + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number +>42 : number + + throw ""; +>"" : string + } + else { + x = ""; +>x = "" : string +>x : string | number +>"" : string + } + x; // string +>x : string +} + diff --git a/tests/baselines/reference/controlFlowWhileStatement.js b/tests/baselines/reference/controlFlowWhileStatement.js new file mode 100644 index 0000000000000..fc975764ce195 --- /dev/null +++ b/tests/baselines/reference/controlFlowWhileStatement.js @@ -0,0 +1,155 @@ +//// [controlFlowWhileStatement.ts] +let cond: boolean; +function a() { + let x: string | number; + x = ""; + while (cond) { + x; // string + } +} +function b() { + let x: string | number; + x = ""; + while (cond) { + x; // string + x = 42; + break; + } +} +function c() { + let x: string | number; + x = ""; + while (cond) { + x; // string + x = undefined; + if (typeof x === "string") continue; + break; + } +} +function d() { + let x: string | number; + x = ""; + while (x = x.length) { + x; // number + x = ""; + } +} +function e() { + let x: string | number; + x = ""; + while (cond) { + x = 42; + } + x; // string | number +} +function f() { + let x: string | number | boolean | RegExp | Function; + x = ""; + while (cond) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // string | number | boolean | RegExp +} +function g() { + let x: string | number | boolean | RegExp | Function; + x = ""; + while (true) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // number +} + + +//// [controlFlowWhileStatement.js] +var cond; +function a() { + var x; + x = ""; + while (cond) { + x; // string + } +} +function b() { + var x; + x = ""; + while (cond) { + x; // string + x = 42; + break; + } +} +function c() { + var x; + x = ""; + while (cond) { + x; // string + x = undefined; + if (typeof x === "string") + continue; + break; + } +} +function d() { + var x; + x = ""; + while (x = x.length) { + x; // number + x = ""; + } +} +function e() { + var x; + x = ""; + while (cond) { + x = 42; + } + x; // string | number +} +function f() { + var x; + x = ""; + while (cond) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // string | number | boolean | RegExp +} +function g() { + var x; + x = ""; + while (true) { + if (cond) { + x = 42; + break; + } + if (cond) { + x = true; + continue; + } + x = /a/; + } + x; // number +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.symbols b/tests/baselines/reference/controlFlowWhileStatement.symbols new file mode 100644 index 0000000000000..27e1b533d145c --- /dev/null +++ b/tests/baselines/reference/controlFlowWhileStatement.symbols @@ -0,0 +1,177 @@ +=== tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + +function a() { +>a : Symbol(a, Decl(controlFlowWhileStatement.ts, 0, 18)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 2, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 2, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x; // string +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 2, 7)) + } +} +function b() { +>b : Symbol(b, Decl(controlFlowWhileStatement.ts, 7, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 9, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 9, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x; // string +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 9, 7)) + + x = 42; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 9, 7)) + + break; + } +} +function c() { +>c : Symbol(c, Decl(controlFlowWhileStatement.ts, 16, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 18, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 18, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x; // string +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 18, 7)) + + x = undefined; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 18, 7)) +>undefined : Symbol(undefined) + + if (typeof x === "string") continue; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 18, 7)) + + break; + } +} +function d() { +>d : Symbol(d, Decl(controlFlowWhileStatement.ts, 26, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) + + while (x = x.length) { +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + + x; // number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 28, 7)) + } +} +function e() { +>e : Symbol(e, Decl(controlFlowWhileStatement.ts, 34, 1)) + + let x: string | number; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) + } + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) +} +function f() { +>f : Symbol(f, Decl(controlFlowWhileStatement.ts, 42, 1)) + + let x: string | number | boolean | RegExp | Function; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + if (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) + + break; + } + if (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = true; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) + + continue; + } + x = /a/; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) + } + x; // string | number | boolean | RegExp +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +} +function g() { +>g : Symbol(g, Decl(controlFlowWhileStatement.ts, 58, 1)) + + let x: string | number | boolean | RegExp | Function; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) + + while (true) { + if (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = 42; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) + + break; + } + if (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = true; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) + + continue; + } + x = /a/; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) + } + x; // number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +} + diff --git a/tests/baselines/reference/controlFlowWhileStatement.types b/tests/baselines/reference/controlFlowWhileStatement.types new file mode 100644 index 0000000000000..7c99b1520486e --- /dev/null +++ b/tests/baselines/reference/controlFlowWhileStatement.types @@ -0,0 +1,216 @@ +=== tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts === +let cond: boolean; +>cond : boolean + +function a() { +>a : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + while (cond) { +>cond : boolean + + x; // string +>x : string + } +} +function b() { +>b : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + while (cond) { +>cond : boolean + + x; // string +>x : string + + x = 42; +>x = 42 : number +>x : string | number +>42 : number + + break; + } +} +function c() { +>c : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + while (cond) { +>cond : boolean + + x; // string +>x : string + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + + if (typeof x === "string") continue; +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + break; + } +} +function d() { +>d : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + while (x = x.length) { +>x = x.length : number +>x : string | number +>x.length : number +>x : string +>length : number + + x; // number +>x : number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + } +} +function e() { +>e : () => void + + let x: string | number; +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + while (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number +>42 : number + } + x; // string | number +>x : string | number +} +function f() { +>f : () => void + + let x: string | number | boolean | RegExp | Function; +>x : string | number | boolean | RegExp | Function +>RegExp : RegExp +>Function : Function + + x = ""; +>x = "" : string +>x : string | number | boolean | RegExp | Function +>"" : string + + while (cond) { +>cond : boolean + + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp | Function +>42 : number + + break; + } + if (cond) { +>cond : boolean + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp | Function +>true : boolean + + continue; + } + x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp | Function +>/a/ : RegExp + } + x; // string | number | boolean | RegExp +>x : string | boolean | RegExp | number +} +function g() { +>g : () => void + + let x: string | number | boolean | RegExp | Function; +>x : string | number | boolean | RegExp | Function +>RegExp : RegExp +>Function : Function + + x = ""; +>x = "" : string +>x : string | number | boolean | RegExp | Function +>"" : string + + while (true) { +>true : boolean + + if (cond) { +>cond : boolean + + x = 42; +>x = 42 : number +>x : string | number | boolean | RegExp | Function +>42 : number + + break; + } + if (cond) { +>cond : boolean + + x = true; +>x = true : boolean +>x : string | number | boolean | RegExp | Function +>true : boolean + + continue; + } + x = /a/; +>x = /a/ : RegExp +>x : string | number | boolean | RegExp | Function +>/a/ : RegExp + } + x; // number +>x : number +} + diff --git a/tests/baselines/reference/for-of45.types b/tests/baselines/reference/for-of45.types index b71e062eccfc2..7c853993b0aef 100644 --- a/tests/baselines/reference/for-of45.types +++ b/tests/baselines/reference/for-of45.types @@ -13,7 +13,7 @@ var map = new Map([["", true]]); >true : boolean for ([k = "", v = false] of map) { ->[k = "", v = false] : (string | boolean)[] +>[k = "", v = false] : [string, boolean] >k = "" : string >k : string >"" : string diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPattern2.types b/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPattern2.types index 33b503d0d8fd0..78cb778b5451b 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPattern2.types +++ b/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPattern2.types @@ -93,7 +93,7 @@ let numberA3: number, robotAInfo: (number | string)[], multiRobotAInfo: (string >multiRobotAInfo : (string | [string, string])[] for ([, nameA] of robots) { ->[, nameA] : string[] +>[, nameA] : [undefined, string] > : undefined >nameA : string >robots : [number, string, string][] @@ -106,7 +106,7 @@ for ([, nameA] of robots) { >nameA : string } for ([, nameA] of getRobots()) { ->[, nameA] : string[] +>[, nameA] : [undefined, string] > : undefined >nameA : string >getRobots() : [number, string, string][] @@ -120,7 +120,7 @@ for ([, nameA] of getRobots()) { >nameA : string } for ([, nameA] of [robotA, robotB]) { ->[, nameA] : string[] +>[, nameA] : [undefined, string] > : undefined >nameA : string >[robotA, robotB] : [number, string, string][] @@ -135,9 +135,9 @@ for ([, nameA] of [robotA, robotB]) { >nameA : string } for ([, [primarySkillA, secondarySkillA]] of multiRobots) { ->[, [primarySkillA, secondarySkillA]] : string[][] +>[, [primarySkillA, secondarySkillA]] : [undefined, [string, string]] > : undefined ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >multiRobots : [string, [string, string]][] @@ -150,9 +150,9 @@ for ([, [primarySkillA, secondarySkillA]] of multiRobots) { >primarySkillA : string } for ([, [primarySkillA, secondarySkillA]] of getMultiRobots()) { ->[, [primarySkillA, secondarySkillA]] : string[][] +>[, [primarySkillA, secondarySkillA]] : [undefined, [string, string]] > : undefined ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >getMultiRobots() : [string, [string, string]][] @@ -166,9 +166,9 @@ for ([, [primarySkillA, secondarySkillA]] of getMultiRobots()) { >primarySkillA : string } for ([, [primarySkillA, secondarySkillA]] of [multiRobotA, multiRobotB]) { ->[, [primarySkillA, secondarySkillA]] : string[][] +>[, [primarySkillA, secondarySkillA]] : [undefined, [string, string]] > : undefined ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >[multiRobotA, multiRobotB] : [string, [string, string]][] @@ -184,7 +184,7 @@ for ([, [primarySkillA, secondarySkillA]] of [multiRobotA, multiRobotB]) { } for ([numberB] of robots) { ->[numberB] : number[] +>[numberB] : [number] >numberB : number >robots : [number, string, string][] @@ -196,7 +196,7 @@ for ([numberB] of robots) { >numberB : number } for ([numberB] of getRobots()) { ->[numberB] : number[] +>[numberB] : [number] >numberB : number >getRobots() : [number, string, string][] >getRobots : () => [number, string, string][] @@ -209,7 +209,7 @@ for ([numberB] of getRobots()) { >numberB : number } for ([numberB] of [robotA, robotB]) { ->[numberB] : number[] +>[numberB] : [number] >numberB : number >[robotA, robotB] : [number, string, string][] >robotA : [number, string, string] @@ -223,7 +223,7 @@ for ([numberB] of [robotA, robotB]) { >numberB : number } for ([nameB] of multiRobots) { ->[nameB] : string[] +>[nameB] : [string] >nameB : string >multiRobots : [string, [string, string]][] @@ -235,7 +235,7 @@ for ([nameB] of multiRobots) { >nameB : string } for ([nameB] of getMultiRobots()) { ->[nameB] : string[] +>[nameB] : [string] >nameB : string >getMultiRobots() : [string, [string, string]][] >getMultiRobots : () => [string, [string, string]][] @@ -248,7 +248,7 @@ for ([nameB] of getMultiRobots()) { >nameB : string } for ([nameB] of [multiRobotA, multiRobotB]) { ->[nameB] : string[] +>[nameB] : [string] >nameB : string >[multiRobotA, multiRobotB] : [string, [string, string]][] >multiRobotA : [string, [string, string]] @@ -263,7 +263,7 @@ for ([nameB] of [multiRobotA, multiRobotB]) { } for ([numberA2, nameA2, skillA2] of robots) { ->[numberA2, nameA2, skillA2] : (number | string)[] +>[numberA2, nameA2, skillA2] : [number, string, string] >numberA2 : number >nameA2 : string >skillA2 : string @@ -277,7 +277,7 @@ for ([numberA2, nameA2, skillA2] of robots) { >nameA2 : string } for ([numberA2, nameA2, skillA2] of getRobots()) { ->[numberA2, nameA2, skillA2] : (number | string)[] +>[numberA2, nameA2, skillA2] : [number, string, string] >numberA2 : number >nameA2 : string >skillA2 : string @@ -292,7 +292,7 @@ for ([numberA2, nameA2, skillA2] of getRobots()) { >nameA2 : string } for ([numberA2, nameA2, skillA2] of [robotA, robotB]) { ->[numberA2, nameA2, skillA2] : (number | string)[] +>[numberA2, nameA2, skillA2] : [number, string, string] >numberA2 : number >nameA2 : string >skillA2 : string @@ -308,9 +308,9 @@ for ([numberA2, nameA2, skillA2] of [robotA, robotB]) { >nameA2 : string } for ([nameMA, [primarySkillA, secondarySkillA]] of multiRobots) { ->[nameMA, [primarySkillA, secondarySkillA]] : (string | string[])[] +>[nameMA, [primarySkillA, secondarySkillA]] : [string, [string, string]] >nameMA : string ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >multiRobots : [string, [string, string]][] @@ -323,9 +323,9 @@ for ([nameMA, [primarySkillA, secondarySkillA]] of multiRobots) { >nameMA : string } for ([nameMA, [primarySkillA, secondarySkillA]] of getMultiRobots()) { ->[nameMA, [primarySkillA, secondarySkillA]] : (string | string[])[] +>[nameMA, [primarySkillA, secondarySkillA]] : [string, [string, string]] >nameMA : string ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >getMultiRobots() : [string, [string, string]][] @@ -339,9 +339,9 @@ for ([nameMA, [primarySkillA, secondarySkillA]] of getMultiRobots()) { >nameMA : string } for ([nameMA, [primarySkillA, secondarySkillA]] of [multiRobotA, multiRobotB]) { ->[nameMA, [primarySkillA, secondarySkillA]] : (string | string[])[] +>[nameMA, [primarySkillA, secondarySkillA]] : [string, [string, string]] >nameMA : string ->[primarySkillA, secondarySkillA] : string[] +>[primarySkillA, secondarySkillA] : [string, string] >primarySkillA : string >secondarySkillA : string >[multiRobotA, multiRobotB] : [string, [string, string]][] diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPatternDefaultValues2.types b/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPatternDefaultValues2.types index 14189ad9d81af..5f580ad38f442 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPatternDefaultValues2.types +++ b/tests/baselines/reference/sourceMapValidationDestructuringForOfArrayBindingPatternDefaultValues2.types @@ -93,7 +93,7 @@ let numberA3: number, robotAInfo: (number | string)[], multiRobotAInfo: (string >multiRobotAInfo : (string | [string, string])[] for ([, nameA = "noName"] of robots) { ->[, nameA = "noName"] : string[] +>[, nameA = "noName"] : [undefined, string] > : undefined >nameA = "noName" : string >nameA : string @@ -108,7 +108,7 @@ for ([, nameA = "noName"] of robots) { >nameA : string } for ([, nameA = "noName"] of getRobots()) { ->[, nameA = "noName"] : string[] +>[, nameA = "noName"] : [undefined, string] > : undefined >nameA = "noName" : string >nameA : string @@ -124,7 +124,7 @@ for ([, nameA = "noName"] of getRobots()) { >nameA : string } for ([, nameA = "noName"] of [robotA, robotB]) { ->[, nameA = "noName"] : string[] +>[, nameA = "noName"] : [undefined, string] > : undefined >nameA = "noName" : string >nameA : string @@ -141,7 +141,7 @@ for ([, nameA = "noName"] of [robotA, robotB]) { >nameA : string } for ([, [ ->[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, string][] +>[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [undefined, [string, string]] > : undefined >[ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"] : [string, string] >[ primarySkillA = "primary", secondarySkillA = "secondary"] : [string, string] @@ -170,7 +170,7 @@ for ([, [ >primarySkillA : string } for ([, [ ->[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, string][] +>[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [undefined, [string, string]] > : undefined >[ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"] : [string, string] >[ primarySkillA = "primary", secondarySkillA = "secondary"] : [string, string] @@ -200,7 +200,7 @@ for ([, [ >primarySkillA : string } for ([, [ ->[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, string][] +>[, [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [undefined, [string, string]] > : undefined >[ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"] : [string, string] >[ primarySkillA = "primary", secondarySkillA = "secondary"] : [string, string] @@ -232,7 +232,7 @@ for ([, [ } for ([numberB = -1] of robots) { ->[numberB = -1] : number[] +>[numberB = -1] : [number] >numberB = -1 : number >numberB : number >-1 : number @@ -247,7 +247,7 @@ for ([numberB = -1] of robots) { >numberB : number } for ([numberB = -1] of getRobots()) { ->[numberB = -1] : number[] +>[numberB = -1] : [number] >numberB = -1 : number >numberB : number >-1 : number @@ -263,7 +263,7 @@ for ([numberB = -1] of getRobots()) { >numberB : number } for ([numberB = -1] of [robotA, robotB]) { ->[numberB = -1] : number[] +>[numberB = -1] : [number] >numberB = -1 : number >numberB : number >-1 : number @@ -280,7 +280,7 @@ for ([numberB = -1] of [robotA, robotB]) { >numberB : number } for ([nameB = "noName"] of multiRobots) { ->[nameB = "noName"] : string[] +>[nameB = "noName"] : [string] >nameB = "noName" : string >nameB : string >"noName" : string @@ -294,7 +294,7 @@ for ([nameB = "noName"] of multiRobots) { >nameB : string } for ([nameB = "noName"] of getMultiRobots()) { ->[nameB = "noName"] : string[] +>[nameB = "noName"] : [string] >nameB = "noName" : string >nameB : string >"noName" : string @@ -309,7 +309,7 @@ for ([nameB = "noName"] of getMultiRobots()) { >nameB : string } for ([nameB = "noName"] of [multiRobotA, multiRobotB]) { ->[nameB = "noName"] : string[] +>[nameB = "noName"] : [string] >nameB = "noName" : string >nameB : string >"noName" : string @@ -326,7 +326,7 @@ for ([nameB = "noName"] of [multiRobotA, multiRobotB]) { } for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of robots) { ->[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : (number | string)[] +>[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : [number, string, string] >numberA2 = -1 : number >numberA2 : number >-1 : number @@ -347,7 +347,7 @@ for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of robots) { >nameA2 : string } for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of getRobots()) { ->[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : (number | string)[] +>[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : [number, string, string] >numberA2 = -1 : number >numberA2 : number >-1 : number @@ -369,7 +369,7 @@ for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of getRobots()) { >nameA2 : string } for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of [robotA, robotB]) { ->[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : (number | string)[] +>[numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] : [number, string, string] >numberA2 = -1 : number >numberA2 : number >-1 : number @@ -392,7 +392,7 @@ for ([numberA2 = -1, nameA2 = "noName", skillA2 = "skill"] of [robotA, robotB]) >nameA2 : string } for ([nameMA = "noName", [ ->[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : (string | [string, string])[] +>[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, [string, string]] >nameMA = "noName" : string >nameMA : string >"noName" : string @@ -423,7 +423,7 @@ for ([nameMA = "noName", [ >nameMA : string } for ([nameMA = "noName", [ ->[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : (string | [string, string])[] +>[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, [string, string]] >nameMA = "noName" : string >nameMA : string >"noName" : string @@ -455,7 +455,7 @@ for ([nameMA = "noName", [ >nameMA : string } for ([nameMA = "noName", [ ->[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : (string | [string, string])[] +>[nameMA = "noName", [ primarySkillA = "primary", secondarySkillA = "secondary"] = ["skill1", "skill2"]] : [string, [string, string]] >nameMA = "noName" : string >nameMA : string >"noName" : string diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForOfObjectBindingPatternDefaultValues2.types b/tests/baselines/reference/sourceMapValidationDestructuringForOfObjectBindingPatternDefaultValues2.types index fe630aafccc7a..adb5d515fe4b0 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForOfObjectBindingPatternDefaultValues2.types +++ b/tests/baselines/reference/sourceMapValidationDestructuringForOfObjectBindingPatternDefaultValues2.types @@ -102,7 +102,7 @@ let name: string, primary: string, secondary: string, skill: string; >skill : string for ({name: nameA = "noName" } of robots) { ->{name: nameA = "noName" } : { name: string; } +>{name: nameA = "noName" } : { name?: string; } >name : Robot >nameA = "noName" : string >nameA : string @@ -117,7 +117,7 @@ for ({name: nameA = "noName" } of robots) { >nameA : string } for ({name: nameA = "noName" } of getRobots()) { ->{name: nameA = "noName" } : { name: string; } +>{name: nameA = "noName" } : { name?: string; } >name : Robot >nameA = "noName" : string >nameA : string @@ -133,7 +133,7 @@ for ({name: nameA = "noName" } of getRobots()) { >nameA : string } for ({name: nameA = "noName" } of [{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }]) { ->{name: nameA = "noName" } : { name: string; } +>{name: nameA = "noName" } : { name?: string; } >name : { name: string; skill: string; } >nameA = "noName" : string >nameA : string @@ -158,7 +158,7 @@ for ({name: nameA = "noName" } of [{ name: "mower", skill: "mowing" }, { name: " >nameA : string } for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = ->{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills?: { primary?: string; secondary?: string; }; } >skills : MultiRobot >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } : { primary?: string; secondary?: string; } >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } : { primary?: string; secondary?: string; } @@ -187,7 +187,7 @@ for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "seconda >primaryA : string } for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = ->{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills?: { primary?: string; secondary?: string; }; } >skills : MultiRobot >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } : { primary?: string; secondary?: string; } >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } : { primary?: string; secondary?: string; } @@ -217,7 +217,7 @@ for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "seconda >primaryA : string } for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = ->{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } } : { skills?: { primary?: string; secondary?: string; }; } >skills : MultiRobot >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "nosKill", secondary: "noSkill" } : { primary?: string; secondary?: string; } >{ primary: primaryA = "primary", secondary: secondaryA = "secondary" } : { primary?: string; secondary?: string; } @@ -271,7 +271,7 @@ for ({ skills: { primary: primaryA = "primary", secondary: secondaryA = "seconda } for ({ name = "noName" } of robots) { ->{ name = "noName" } : { name: string; } +>{ name = "noName" } : { name?: string; } >name : Robot >robots : Robot[] @@ -283,7 +283,7 @@ for ({ name = "noName" } of robots) { >nameA : string } for ({ name = "noName" } of getRobots()) { ->{ name = "noName" } : { name: string; } +>{ name = "noName" } : { name?: string; } >name : Robot >getRobots() : Robot[] >getRobots : () => Robot[] @@ -296,7 +296,7 @@ for ({ name = "noName" } of getRobots()) { >nameA : string } for ({ name = "noName" } of [{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }]) { ->{ name = "noName" } : { name: string; } +>{ name = "noName" } : { name?: string; } >name : { name: string; skill: string; } >[{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }] : { name: string; skill: string; }[] >{ name: "mower", skill: "mowing" } : { name: string; skill: string; } @@ -318,7 +318,7 @@ for ({ name = "noName" } of [{ name: "mower", skill: "mowing" }, { name: "trimme >nameA : string } for ({ ->{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills?: { primary?: string; secondary?: string; }; } skills: { >skills : MultiRobot @@ -349,7 +349,7 @@ for ({ >primaryA : string } for ({ ->{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills?: { primary?: string; secondary?: string; }; } skills: { >skills : MultiRobot @@ -381,7 +381,7 @@ for ({ >primaryA : string } for ({ ->{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills: { primary?: string; secondary?: string; }; } +>{ skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { skills?: { primary?: string; secondary?: string; }; } skills: { >skills : { name: string; skills: { primary: string; secondary: string; }; } @@ -434,7 +434,7 @@ for ({ for ({name: nameA = "noName", skill: skillA = "noSkill" } of robots) { ->{name: nameA = "noName", skill: skillA = "noSkill" } : { name: string; skill: string; } +>{name: nameA = "noName", skill: skillA = "noSkill" } : { name?: string; skill?: string; } >name : Robot >nameA = "noName" : string >nameA : string @@ -453,7 +453,7 @@ for ({name: nameA = "noName", skill: skillA = "noSkill" } of robots) { >nameA : string } for ({name: nameA = "noName", skill: skillA = "noSkill" } of getRobots()) { ->{name: nameA = "noName", skill: skillA = "noSkill" } : { name: string; skill: string; } +>{name: nameA = "noName", skill: skillA = "noSkill" } : { name?: string; skill?: string; } >name : Robot >nameA = "noName" : string >nameA : string @@ -473,7 +473,7 @@ for ({name: nameA = "noName", skill: skillA = "noSkill" } of getRobots()) { >nameA : string } for ({name: nameA = "noName", skill: skillA = "noSkill" } of [{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }]) { ->{name: nameA = "noName", skill: skillA = "noSkill" } : { name: string; skill: string; } +>{name: nameA = "noName", skill: skillA = "noSkill" } : { name?: string; skill?: string; } >name : { name: string; skill: string; } >nameA = "noName" : string >nameA : string @@ -502,7 +502,7 @@ for ({name: nameA = "noName", skill: skillA = "noSkill" } of [{ name: "mower", >nameA : string } for ({ ->{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name: nameA = "noName", >name : MultiRobot @@ -545,7 +545,7 @@ for ({ >nameA : string } for ({ ->{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name: nameA = "noName", >name : MultiRobot @@ -589,7 +589,7 @@ for ({ >nameA : string } for ({ ->{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name: nameA = "noName", skills: { primary: primaryA = "primary", secondary: secondaryA = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name: nameA = "noName", >name : MultiRobot @@ -655,7 +655,7 @@ for ({ } for ({ name = "noName", skill = "noSkill" } of robots) { ->{ name = "noName", skill = "noSkill" } : { name: string; skill: string; } +>{ name = "noName", skill = "noSkill" } : { name?: string; skill?: string; } >name : Robot >skill : Robot >robots : Robot[] @@ -668,7 +668,7 @@ for ({ name = "noName", skill = "noSkill" } of robots) { >nameA : string } for ({ name = "noName", skill = "noSkill" } of getRobots()) { ->{ name = "noName", skill = "noSkill" } : { name: string; skill: string; } +>{ name = "noName", skill = "noSkill" } : { name?: string; skill?: string; } >name : Robot >skill : Robot >getRobots() : Robot[] @@ -682,7 +682,7 @@ for ({ name = "noName", skill = "noSkill" } of getRobots()) { >nameA : string } for ({ name = "noName", skill = "noSkill" } of [{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }]) { ->{ name = "noName", skill = "noSkill" } : { name: string; skill: string; } +>{ name = "noName", skill = "noSkill" } : { name?: string; skill?: string; } >name : { name: string; skill: string; } >skill : { name: string; skill: string; } >[{ name: "mower", skill: "mowing" }, { name: "trimmer", skill: "trimming" }] : { name: string; skill: string; }[] @@ -705,7 +705,7 @@ for ({ name = "noName", skill = "noSkill" } of [{ name: "mower", skill: "mowing >nameA : string } for ({ ->{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name = "noName", >name : MultiRobot @@ -739,7 +739,7 @@ for ({ >nameA : string } for ({ ->{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name = "noName", >name : MultiRobot @@ -774,7 +774,7 @@ for ({ >nameA : string } for ({ ->{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name: string; skills: { primary?: string; secondary?: string; }; } +>{ name = "noName", skills: { primary = "primary", secondary = "secondary" } = { primary: "noSkill", secondary: "noSkill" }} : { name?: string; skills?: { primary?: string; secondary?: string; }; } name = "noName", >name : { name: string; skills: { primary: string; secondary: string; }; } diff --git a/tests/baselines/reference/systemModule13.types b/tests/baselines/reference/systemModule13.types index 35f22be98b8ea..f31b082541613 100644 --- a/tests/baselines/reference/systemModule13.types +++ b/tests/baselines/reference/systemModule13.types @@ -24,7 +24,7 @@ export const {a: z0, b: {c: z1}} = {a: true, b: {c: "123"}}; >"123" : string for ([x] of [[1]]) {} ->[x] : number[] +>[x] : [number] >x : number >[[1]] : number[][] >[1] : number[] diff --git a/tests/baselines/reference/systemModule8.types b/tests/baselines/reference/systemModule8.types index 2c3dd1e48bb04..940067c5653ee 100644 --- a/tests/baselines/reference/systemModule8.types +++ b/tests/baselines/reference/systemModule8.types @@ -135,7 +135,7 @@ export const {a: z0, b: {c: z1}} = {a: true, b: {c: "123"}}; >"123" : string for ([x] of [[1]]) {} ->[x] : any[] +>[x] : [any] >x : any >[[1]] : number[][] >[1] : number[] From 0820249e710e3c148eea558388afc7d5e6559f98 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 26 Mar 2016 08:20:06 -0700 Subject: [PATCH 13/67] Fixing some tests --- .../typeGuardsInConditionalExpression.ts | 48 ++++++++----------- .../typeGuards/typeGuardsInIfStatement.ts | 35 ++++++-------- ...ypeGuardsInRightOperandOfAndAndOperator.ts | 19 ++------ .../typeGuardsInRightOperandOfOrOrOperator.ts | 16 ++----- .../unionTypesAssignability.ts | 19 ++++---- 5 files changed, 51 insertions(+), 86 deletions(-) diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts index 1633c80ab67b0..01f9361ebc3d0 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts @@ -11,43 +11,37 @@ function foo(x: number | string) { : x++; // number } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed return typeof x === "string" - ? (x = 10 && x)// string | number - : x; // string | number + ? ((x = "hello") && x) // string + : x; // number } function foo3(x: number | string) { - // x is assigned in the if false branch, the type is not narrowed - // even though assigned using same type as narrowed expression return typeof x === "string" - ? (x = "Hello" && x) // string | number - : x; // string | number + ? ((x = 10) && x) // number + : x; // number } function foo4(x: number | string) { - // false branch updates the variable - so here it is not number - // even though assigned using same type as narrowed expression return typeof x === "string" - ? x // string | number - : (x = 10 && x); // string | number + ? x // string + : ((x = 10) && x); // number } function foo5(x: number | string) { - // false branch updates the variable - so here it is not number return typeof x === "string" - ? x // string | number - : (x = "hello" && x); // string | number + ? x // string + : ((x = "hello") && x); // string } function foo6(x: number | string) { // Modify in both branches return typeof x === "string" - ? (x = 10 && x) // string | number - : (x = "hello" && x); // string | number + ? ((x = 10) && x) // number + : ((x = "hello") && x); // string } function foo7(x: number | string | boolean) { return typeof x === "string" - ? x === "hello" // string + ? x === "hello" // boolean : typeof x === "boolean" ? x // boolean - : x == 10; // number + : x == 10; // boolean } function foo8(x: number | string | boolean) { var b: number | boolean; @@ -56,14 +50,14 @@ function foo8(x: number | string | boolean) { : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean - : x == 10)); // number + : x == 10)); // boolean } function foo9(x: number | string) { var y = 10; // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop return typeof x === "string" - ? ((y = x.length) && x === "hello") // string - : x === 10; // number + ? ((y = x.length) && x === "hello") // boolean + : x === 10; // boolean } function foo10(x: number | string | boolean) { // Mixing typeguards @@ -76,22 +70,20 @@ function foo10(x: number | string | boolean) { } function foo11(x: number | string | boolean) { // Mixing typeguards - // Assigning value to x deep inside another guard stops narrowing of type too var b: number | boolean | string; return typeof x === "string" - ? x // number | boolean | string - changed in the false branch - : ((b = x) // x is number | boolean | string - because the assignment changed it + ? x // string + : ((b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x - && x); // x is number | boolean | string + && x); // x is number } function foo12(x: number | string | boolean) { // Mixing typeguards - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression var b: number | boolean | string; return typeof x === "string" - ? (x = 10 && x.toString().length) // number | boolean | string - changed here - : ((b = x) // x is number | boolean | string - changed in true branch + ? ((x = 10) && x.toString().length) // number + : ((b = x) // x is number | boolean && typeof x === "number" && x); // x is number } \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts index f72845c18a4a9..1626cfdb82e99 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts @@ -1,9 +1,7 @@ // In the true branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true, -// provided the true branch statement contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. // In the false branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false, -// provided the false branch statement contains no assignments to the variable or parameter +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false. function foo(x: number | string) { if (typeof x === "string") { return x.length; // string @@ -13,54 +11,49 @@ function foo(x: number | string) { } } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { - return x; // string | number + return x; // number } } function foo3(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x; // string } else { - return x; // string | number + return x; // number } } function foo4(x: number | string) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { - x = 10; // even though assigned number - this should result in x to be string | number - return x; // string | number + x = 10; + return x; // number } } function foo5(x: number | string) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { x = "hello"; - return x; // string | number + return x; // string } } function foo6(x: number | string) { - // Modify in both branches if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { x = "hello"; - return x; // string | number + return x; // string } } function foo7(x: number | string | boolean) { diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts index da0d8ce24b5a3..acd71f37d9132 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts @@ -1,6 +1,5 @@ // In the right operand of a && operation, -// the type of a variable or parameter is narrowed by any type guard in the left operand when true, -// provided the right operand contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the left operand when true. function foo(x: number | string) { return typeof x === "string" && x.length === 10; // string } @@ -35,21 +34,11 @@ function foo7(x: number | string | boolean) { var y: number| boolean | string; var z: number| boolean | string; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x !== "string" - && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && ((z = x) // number | boolean && (typeof x === "number" // change value of x - ? (x = 10 && x.toString()) // number | boolean | string + ? ((x = 10) && x.toString()) // x is number // do not change value - : (y = x && x.toString()))); // number | boolean | string + : ((y = x) && x.toString()))); // x is boolean } -function foo8(x: number | string) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" - && (x = 10) // change x - number| string - && (typeof x === "number" - ? x // number - : x.length); // string -} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts index 867dc143a85a5..131e5dd10bcaf 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfOrOrOperator.ts @@ -35,21 +35,11 @@ function foo7(x: number | string | boolean) { var y: number| boolean | string; var z: number| boolean | string; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x === "string" - || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || ((z = x) // number | boolean || (typeof x === "number" // change value of x - ? (x = 10 && x.toString()) // number | boolean | string + ? ((x = 10) && x.toString()) // number | boolean | string // do not change value - : (y = x && x.toString()))); // number | boolean | string + : ((y = x) && x.toString()))); // number | boolean | string } -function foo8(x: number | string) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" - || (x = 10) // change x - number| string - || (typeof x === "number" - ? x // number - : x.length); // string -} \ No newline at end of file diff --git a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts index 9ceee8845a58a..90f16b6d15d53 100644 --- a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts +++ b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts @@ -59,13 +59,14 @@ unionNumberString = null; unionDE = undefined; unionNumberString = undefined; -// type parameters -function foo(t: T, u: U) { - t = u; // error - u = t; // error - var x : T | U; - x = t; // ok - x = u; // ok - t = x; // error U not assignable to T - u = x; // error T not assignable to U +// type parameters +function foo(t: T, u: U) { + t = u; // error + u = t; // error + var x : T | U; + x = t; // ok + x = u; // ok + x = undefined; + t = x; // error U not assignable to T + u = x; // error T not assignable to U } From 5a5d89a71e3842366086bf2fda3b45394f34cc05 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 26 Mar 2016 08:21:43 -0700 Subject: [PATCH 14/67] Accepting new baselines --- .../reference/typeAssertions.errors.txt | 11 +- .../typeGuardsInConditionalExpression.js | 96 ++--- .../typeGuardsInConditionalExpression.symbols | 210 +++++---- .../typeGuardsInConditionalExpression.types | 185 ++++---- .../reference/typeGuardsInDoStatement.js | 60 +++ .../reference/typeGuardsInDoStatement.symbols | 74 ++++ .../reference/typeGuardsInDoStatement.types | 92 ++++ .../reference/typeGuardsInForStatement.js | 48 +++ .../typeGuardsInForStatement.symbols | 62 +++ .../reference/typeGuardsInForStatement.types | 77 ++++ .../typeGuardsInIfStatement.errors.txt | 153 +++++++ .../reference/typeGuardsInIfStatement.js | 70 ++- .../reference/typeGuardsInIfStatement.symbols | 304 ------------- .../reference/typeGuardsInIfStatement.types | 405 ------------------ ...ypeGuardsInRightOperandOfAndAndOperator.js | 39 +- ...ardsInRightOperandOfAndAndOperator.symbols | 130 +++--- ...GuardsInRightOperandOfAndAndOperator.types | 107 ++--- .../typeGuardsInRightOperandOfOrOrOperator.js | 33 +- ...GuardsInRightOperandOfOrOrOperator.symbols | 37 +- ...peGuardsInRightOperandOfOrOrOperator.types | 104 ++--- .../reference/typeGuardsInWhileStatement.js | 54 +++ .../typeGuardsInWhileStatement.symbols | 62 +++ .../typeGuardsInWhileStatement.types | 74 ++++ .../unionTypesAssignability.errors.txt | 5 +- .../reference/unionTypesAssignability.js | 2 + 25 files changed, 1179 insertions(+), 1315 deletions(-) create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.js create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.types create mode 100644 tests/baselines/reference/typeGuardsInForStatement.js create mode 100644 tests/baselines/reference/typeGuardsInForStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInForStatement.types create mode 100644 tests/baselines/reference/typeGuardsInIfStatement.errors.txt delete mode 100644 tests/baselines/reference/typeGuardsInIfStatement.symbols delete mode 100644 tests/baselines/reference/typeGuardsInIfStatement.types create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.js create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.types diff --git a/tests/baselines/reference/typeAssertions.errors.txt b/tests/baselines/reference/typeAssertions.errors.txt index c7401e4fcf975..9a9644d8b8fa4 100644 --- a/tests/baselines/reference/typeAssertions.errors.txt +++ b/tests/baselines/reference/typeAssertions.errors.txt @@ -1,10 +1,13 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(5,5): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. Property 'p' is missing in type 'SomeOther'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. Property 'x' is missing in type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(37,13): error TS2352: Neither type 'SomeDerived' nor type 'SomeOther' is assignable to the other. Property 'q' is missing in type 'SomeDerived'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. Property 'q' is missing in type 'SomeBase'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,5): error TS2304: Cannot find name 'numOrStr'. @@ -23,7 +26,7 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,44): err tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): error TS1005: ';' expected. -==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (18 errors) ==== +==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (21 errors) ==== // Function call whose argument is a 1 arg generic function call with explicit type arguments function fn1(t: T) { } function fn2(t: any) { } @@ -58,6 +61,8 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err someBase = someBase; someBase = someOther; // Error ~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. + ~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. !!! error TS2352: Property 'p' is missing in type 'SomeOther'. @@ -65,6 +70,8 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err someDerived = someBase; someDerived = someOther; // Error ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. !!! error TS2352: Property 'x' is missing in type 'SomeOther'. @@ -74,6 +81,8 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err !!! error TS2352: Property 'q' is missing in type 'SomeDerived'. someOther = someBase; // Error ~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. + ~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. !!! error TS2352: Property 'q' is missing in type 'SomeBase'. someOther = someOther; diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js index 118ebbc02c09d..9aade91612a12 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.js +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -12,43 +12,37 @@ function foo(x: number | string) { : x++; // number } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed return typeof x === "string" - ? (x = 10 && x)// string | number - : x; // string | number + ? ((x = "hello") && x) // string + : x; // number } function foo3(x: number | string) { - // x is assigned in the if false branch, the type is not narrowed - // even though assigned using same type as narrowed expression return typeof x === "string" - ? (x = "Hello" && x) // string | number - : x; // string | number + ? ((x = 10) && x) // number + : x; // number } function foo4(x: number | string) { - // false branch updates the variable - so here it is not number - // even though assigned using same type as narrowed expression return typeof x === "string" - ? x // string | number - : (x = 10 && x); // string | number + ? x // string + : ((x = 10) && x); // number } function foo5(x: number | string) { - // false branch updates the variable - so here it is not number return typeof x === "string" - ? x // string | number - : (x = "hello" && x); // string | number + ? x // string + : ((x = "hello") && x); // string } function foo6(x: number | string) { // Modify in both branches return typeof x === "string" - ? (x = 10 && x) // string | number - : (x = "hello" && x); // string | number + ? ((x = 10) && x) // number + : ((x = "hello") && x); // string } function foo7(x: number | string | boolean) { return typeof x === "string" - ? x === "hello" // string + ? x === "hello" // boolean : typeof x === "boolean" ? x // boolean - : x == 10; // number + : x == 10; // boolean } function foo8(x: number | string | boolean) { var b: number | boolean; @@ -57,14 +51,14 @@ function foo8(x: number | string | boolean) { : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean - : x == 10)); // number + : x == 10)); // boolean } function foo9(x: number | string) { var y = 10; // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop return typeof x === "string" - ? ((y = x.length) && x === "hello") // string - : x === 10; // number + ? ((y = x.length) && x === "hello") // boolean + : x === 10; // boolean } function foo10(x: number | string | boolean) { // Mixing typeguards @@ -77,22 +71,20 @@ function foo10(x: number | string | boolean) { } function foo11(x: number | string | boolean) { // Mixing typeguards - // Assigning value to x deep inside another guard stops narrowing of type too var b: number | boolean | string; return typeof x === "string" - ? x // number | boolean | string - changed in the false branch - : ((b = x) // x is number | boolean | string - because the assignment changed it + ? x // string + : ((b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x - && x); // x is number | boolean | string + && x); // x is number } function foo12(x: number | string | boolean) { // Mixing typeguards - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression var b: number | boolean | string; return typeof x === "string" - ? (x = 10 && x.toString().length) // number | boolean | string - changed here - : ((b = x) // x is number | boolean | string - changed in true branch + ? ((x = 10) && x.toString().length) // number + : ((b = x) // x is number | boolean && typeof x === "number" && x); // x is number } @@ -110,43 +102,37 @@ function foo(x) { : x++; // number } function foo2(x) { - // x is assigned in the if true branch, the type is not narrowed return typeof x === "string" - ? (x = 10 && x) // string | number - : x; // string | number + ? ((x = "hello") && x) // string + : x; // number } function foo3(x) { - // x is assigned in the if false branch, the type is not narrowed - // even though assigned using same type as narrowed expression return typeof x === "string" - ? (x = "Hello" && x) // string | number - : x; // string | number + ? ((x = 10) && x) // number + : x; // number } function foo4(x) { - // false branch updates the variable - so here it is not number - // even though assigned using same type as narrowed expression return typeof x === "string" - ? x // string | number - : (x = 10 && x); // string | number + ? x // string + : ((x = 10) && x); // number } function foo5(x) { - // false branch updates the variable - so here it is not number return typeof x === "string" - ? x // string | number - : (x = "hello" && x); // string | number + ? x // string + : ((x = "hello") && x); // string } function foo6(x) { // Modify in both branches return typeof x === "string" - ? (x = 10 && x) // string | number - : (x = "hello" && x); // string | number + ? ((x = 10) && x) // number + : ((x = "hello") && x); // string } function foo7(x) { return typeof x === "string" - ? x === "hello" // string + ? x === "hello" // boolean : typeof x === "boolean" ? x // boolean - : x == 10; // number + : x == 10; // boolean } function foo8(x) { var b; @@ -155,14 +141,14 @@ function foo8(x) { : ((b = x) && (typeof x === "boolean" ? x // boolean - : x == 10)); // number + : x == 10)); // boolean } function foo9(x) { var y = 10; // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop return typeof x === "string" - ? ((y = x.length) && x === "hello") // string - : x === 10; // number + ? ((y = x.length) && x === "hello") // boolean + : x === 10; // boolean } function foo10(x) { // Mixing typeguards @@ -175,22 +161,20 @@ function foo10(x) { } function foo11(x) { // Mixing typeguards - // Assigning value to x deep inside another guard stops narrowing of type too var b; return typeof x === "string" - ? x // number | boolean | string - changed in the false branch - : ((b = x) // x is number | boolean | string - because the assignment changed it + ? x // string + : ((b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x - && x); // x is number | boolean | string + && x); // x is number } function foo12(x) { // Mixing typeguards - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression var b; return typeof x === "string" - ? (x = 10 && x.toString().length) // number | boolean | string - changed here - : ((b = x) // x is number | boolean | string - changed in true branch + ? ((x = 10) && x.toString().length) // number + : ((b = x) // x is number | boolean && typeof x === "number" && x); // x is number } diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.symbols b/tests/baselines/reference/typeGuardsInConditionalExpression.symbols index 7de63d90bf92d..ada6a6d99fd56 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.symbols +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.symbols @@ -25,227 +25,219 @@ function foo2(x: number | string) { >foo2 : Symbol(foo2, Decl(typeGuardsInConditionalExpression.ts, 11, 1)) >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 12, 14)) - // x is assigned in the if true branch, the type is not narrowed return typeof x === "string" >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 12, 14)) - ? (x = 10 && x)// string | number + ? ((x = "hello") && x) // string >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 12, 14)) >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 12, 14)) - : x; // string | number + : x; // number >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 12, 14)) } function foo3(x: number | string) { ->foo3 : Symbol(foo3, Decl(typeGuardsInConditionalExpression.ts, 17, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 18, 14)) +>foo3 : Symbol(foo3, Decl(typeGuardsInConditionalExpression.ts, 16, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 17, 14)) - // x is assigned in the if false branch, the type is not narrowed - // even though assigned using same type as narrowed expression return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 18, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 17, 14)) - ? (x = "Hello" && x) // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 18, 14)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 18, 14)) + ? ((x = 10) && x) // number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 17, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 17, 14)) - : x; // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 18, 14)) + : x; // number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 17, 14)) } function foo4(x: number | string) { ->foo4 : Symbol(foo4, Decl(typeGuardsInConditionalExpression.ts, 24, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 25, 14)) +>foo4 : Symbol(foo4, Decl(typeGuardsInConditionalExpression.ts, 21, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 22, 14)) - // false branch updates the variable - so here it is not number - // even though assigned using same type as narrowed expression return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 25, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 22, 14)) - ? x // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 25, 14)) + ? x // string +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 22, 14)) - : (x = 10 && x); // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 25, 14)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 25, 14)) + : ((x = 10) && x); // number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 22, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 22, 14)) } function foo5(x: number | string) { ->foo5 : Symbol(foo5, Decl(typeGuardsInConditionalExpression.ts, 31, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) +>foo5 : Symbol(foo5, Decl(typeGuardsInConditionalExpression.ts, 26, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 27, 14)) - // false branch updates the variable - so here it is not number return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 27, 14)) - ? x // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) + ? x // string +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 27, 14)) - : (x = "hello" && x); // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) + : ((x = "hello") && x); // string +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 27, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 27, 14)) } function foo6(x: number | string) { ->foo6 : Symbol(foo6, Decl(typeGuardsInConditionalExpression.ts, 37, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) +>foo6 : Symbol(foo6, Decl(typeGuardsInConditionalExpression.ts, 31, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) // Modify in both branches return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) - ? (x = 10 && x) // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) + ? ((x = 10) && x) // number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) - : (x = "hello" && x); // string | number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) + : ((x = "hello") && x); // string +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 32, 14)) } function foo7(x: number | string | boolean) { ->foo7 : Symbol(foo7, Decl(typeGuardsInConditionalExpression.ts, 43, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) +>foo7 : Symbol(foo7, Decl(typeGuardsInConditionalExpression.ts, 37, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) - ? x === "hello" // string ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) + ? x === "hello" // boolean +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) : typeof x === "boolean" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) ? x // boolean ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) - : x == 10; // number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 44, 14)) + : x == 10; // boolean +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 38, 14)) } function foo8(x: number | string | boolean) { ->foo8 : Symbol(foo8, Decl(typeGuardsInConditionalExpression.ts, 50, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>foo8 : Symbol(foo8, Decl(typeGuardsInConditionalExpression.ts, 44, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) var b: number | boolean; ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 52, 7)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 46, 7)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) ? x === "hello" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) : ((b = x) && // number | boolean ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 52, 7)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 46, 7)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) (typeof x === "boolean" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) ? x // boolean ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) - : x == 10)); // number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 51, 14)) + : x == 10)); // boolean +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 45, 14)) } function foo9(x: number | string) { ->foo9 : Symbol(foo9, Decl(typeGuardsInConditionalExpression.ts, 59, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 60, 14)) +>foo9 : Symbol(foo9, Decl(typeGuardsInConditionalExpression.ts, 53, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 54, 14)) var y = 10; ->y : Symbol(y, Decl(typeGuardsInConditionalExpression.ts, 61, 7)) +>y : Symbol(y, Decl(typeGuardsInConditionalExpression.ts, 55, 7)) // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 60, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 54, 14)) - ? ((y = x.length) && x === "hello") // string ->y : Symbol(y, Decl(typeGuardsInConditionalExpression.ts, 61, 7)) + ? ((y = x.length) && x === "hello") // boolean +>y : Symbol(y, Decl(typeGuardsInConditionalExpression.ts, 55, 7)) >x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 60, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 54, 14)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 60, 14)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 54, 14)) - : x === 10; // number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 60, 14)) + : x === 10; // boolean +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 54, 14)) } function foo10(x: number | string | boolean) { ->foo10 : Symbol(foo10, Decl(typeGuardsInConditionalExpression.ts, 66, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>foo10 : Symbol(foo10, Decl(typeGuardsInConditionalExpression.ts, 60, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) // Mixing typeguards var b: boolean | number; ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 69, 7)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 63, 7)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) ? x // string ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) : ((b = x) // x is number | boolean ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 69, 7)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 63, 7)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) && typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) && x.toString()); // x is number >x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 67, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 61, 15)) >toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) } function foo11(x: number | string | boolean) { ->foo11 : Symbol(foo11, Decl(typeGuardsInConditionalExpression.ts, 75, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) +>foo11 : Symbol(foo11, Decl(typeGuardsInConditionalExpression.ts, 69, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) // Mixing typeguards - // Assigning value to x deep inside another guard stops narrowing of type too var b: number | boolean | string; ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 79, 7)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 72, 7)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) - ? x // number | boolean | string - changed in the false branch ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) + ? x // string +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) - : ((b = x) // x is number | boolean | string - because the assignment changed it ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 79, 7)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) + : ((b = x) // x is number | boolean +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 72, 7)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) && typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) && (x = 10) // assignment to x ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) - && x); // x is number | boolean | string ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 76, 15)) + && x); // x is number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 70, 15)) } function foo12(x: number | string | boolean) { ->foo12 : Symbol(foo12, Decl(typeGuardsInConditionalExpression.ts, 86, 1)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>foo12 : Symbol(foo12, Decl(typeGuardsInConditionalExpression.ts, 79, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) // Mixing typeguards - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression var b: number | boolean | string; ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 90, 7)) +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 82, 7)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) - ? (x = 10 && x.toString().length) // number | boolean | string - changed here ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) + ? ((x = 10) && x.toString().length) // number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) >x.toString().length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) - : ((b = x) // x is number | boolean | string - changed in true branch ->b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 90, 7)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) + : ((b = x) // x is number | boolean +>b : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 82, 7)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) && typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) && x); // x is number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 80, 15)) } diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.types b/tests/baselines/reference/typeGuardsInConditionalExpression.types index 493a8c0a31afd..cc1459738e4d8 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.types +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -27,98 +27,96 @@ function foo(x: number | string) { >x : number } function foo2(x: number | string) { ->foo2 : (x: number | string) => number | string +>foo2 : (x: number | string) => string | number >x : number | string - // x is assigned in the if true branch, the type is not narrowed return typeof x === "string" ->typeof x === "string" ? (x = 10 && x)// string | number : x : number | string +>typeof x === "string" ? ((x = "hello") && x) // string : x : string | number >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? (x = 10 && x)// string | number ->(x = 10 && x) : number | string ->x = 10 && x : number | string ->x : number | string ->10 && x : number | string ->10 : number + ? ((x = "hello") && x) // string +>((x = "hello") && x) : string +>(x = "hello") && x : string +>(x = "hello") : string +>x = "hello" : string >x : number | string +>"hello" : string +>x : string - : x; // string | number ->x : number | string + : x; // number +>x : number } function foo3(x: number | string) { ->foo3 : (x: number | string) => number | string +>foo3 : (x: number | string) => number >x : number | string - // x is assigned in the if false branch, the type is not narrowed - // even though assigned using same type as narrowed expression return typeof x === "string" ->typeof x === "string" ? (x = "Hello" && x) // string | number : x : number | string +>typeof x === "string" ? ((x = 10) && x) // number : x : number >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? (x = "Hello" && x) // string | number ->(x = "Hello" && x) : number | string ->x = "Hello" && x : number | string ->x : number | string ->"Hello" && x : number | string ->"Hello" : string + ? ((x = 10) && x) // number +>((x = 10) && x) : number +>(x = 10) && x : number +>(x = 10) : number +>x = 10 : number >x : number | string +>10 : number +>x : number - : x; // string | number ->x : number | string + : x; // number +>x : number } function foo4(x: number | string) { ->foo4 : (x: number | string) => number | string +>foo4 : (x: number | string) => string | number >x : number | string - // false branch updates the variable - so here it is not number - // even though assigned using same type as narrowed expression return typeof x === "string" ->typeof x === "string" ? x // string | number : (x = 10 && x) : number | string +>typeof x === "string" ? x // string : ((x = 10) && x) : string | number >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? x // string | number ->x : number | string + ? x // string +>x : string - : (x = 10 && x); // string | number ->(x = 10 && x) : number | string ->x = 10 && x : number | string + : ((x = 10) && x); // number +>((x = 10) && x) : number +>(x = 10) && x : number +>(x = 10) : number +>x = 10 : number >x : number | string ->10 && x : number | string >10 : number ->x : number | string +>x : number } function foo5(x: number | string) { ->foo5 : (x: number | string) => number | string +>foo5 : (x: number | string) => string >x : number | string - // false branch updates the variable - so here it is not number return typeof x === "string" ->typeof x === "string" ? x // string | number : (x = "hello" && x) : number | string +>typeof x === "string" ? x // string : ((x = "hello") && x) : string >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? x // string | number ->x : number | string + ? x // string +>x : string - : (x = "hello" && x); // string | number ->(x = "hello" && x) : number | string ->x = "hello" && x : number | string + : ((x = "hello") && x); // string +>((x = "hello") && x) : string +>(x = "hello") && x : string +>(x = "hello") : string +>x = "hello" : string >x : number | string ->"hello" && x : number | string >"hello" : string ->x : number | string +>x : string } function foo6(x: number | string) { >foo6 : (x: number | string) => number | string @@ -126,40 +124,42 @@ function foo6(x: number | string) { // Modify in both branches return typeof x === "string" ->typeof x === "string" ? (x = 10 && x) // string | number : (x = "hello" && x) : number | string +>typeof x === "string" ? ((x = 10) && x) // number : ((x = "hello") && x) : number | string >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? (x = 10 && x) // string | number ->(x = 10 && x) : number | string ->x = 10 && x : number | string + ? ((x = 10) && x) // number +>((x = 10) && x) : number +>(x = 10) && x : number +>(x = 10) : number +>x = 10 : number >x : number | string ->10 && x : number | string >10 : number ->x : number | string +>x : number - : (x = "hello" && x); // string | number ->(x = "hello" && x) : number | string ->x = "hello" && x : number | string + : ((x = "hello") && x); // string +>((x = "hello") && x) : string +>(x = "hello") && x : string +>(x = "hello") : string +>x = "hello" : string >x : number | string ->"hello" && x : number | string >"hello" : string ->x : number | string +>x : string } function foo7(x: number | string | boolean) { >foo7 : (x: number | string | boolean) => boolean >x : number | string | boolean return typeof x === "string" ->typeof x === "string" ? x === "hello" // string : typeof x === "boolean" ? x // boolean : x == 10 : boolean +>typeof x === "string" ? x === "hello" // boolean : typeof x === "boolean" ? x // boolean : x == 10 : boolean >typeof x === "string" : boolean >typeof x : string >x : number | string | boolean >"string" : string - ? x === "hello" // string + ? x === "hello" // boolean >x === "hello" : boolean >x : string >"hello" : string @@ -174,7 +174,7 @@ function foo7(x: number | string | boolean) { ? x // boolean >x : boolean - : x == 10; // number + : x == 10; // boolean >x == 10 : boolean >x : number >10 : number @@ -217,7 +217,7 @@ function foo8(x: number | string | boolean) { ? x // boolean >x : boolean - : x == 10)); // number + : x == 10)); // boolean >x == 10 : boolean >x : number >10 : number @@ -232,13 +232,13 @@ function foo9(x: number | string) { // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop return typeof x === "string" ->typeof x === "string" ? ((y = x.length) && x === "hello") // string : x === 10 : boolean +>typeof x === "string" ? ((y = x.length) && x === "hello") // boolean : x === 10 : boolean >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - ? ((y = x.length) && x === "hello") // string + ? ((y = x.length) && x === "hello") // boolean >((y = x.length) && x === "hello") : boolean >(y = x.length) && x === "hello" : boolean >(y = x.length) : number @@ -251,7 +251,7 @@ function foo9(x: number | string) { >x : string >"hello" : string - : x === 10; // number + : x === 10; // boolean >x === 10 : boolean >x : number >10 : number @@ -296,38 +296,37 @@ function foo10(x: number | string | boolean) { >toString : (radix?: number) => string } function foo11(x: number | string | boolean) { ->foo11 : (x: number | string | boolean) => number | string | boolean +>foo11 : (x: number | string | boolean) => string | number >x : number | string | boolean // Mixing typeguards - // Assigning value to x deep inside another guard stops narrowing of type too var b: number | boolean | string; >b : number | boolean | string return typeof x === "string" ->typeof x === "string" ? x // number | boolean | string - changed in the false branch : ((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x) : number | string | boolean +>typeof x === "string" ? x // string : ((b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x && x) : string | number >typeof x === "string" : boolean >typeof x : string >x : number | string | boolean >"string" : string - ? x // number | boolean | string - changed in the false branch ->x : number | string | boolean + ? x // string +>x : string - : ((b = x) // x is number | boolean | string - because the assignment changed it ->((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x) : number | string | boolean ->(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x : number | string | boolean ->(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) : number ->(b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" : boolean ->(b = x) : number | string | boolean ->b = x : number | string | boolean + : ((b = x) // x is number | boolean +>((b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x && x) : number +>(b = x) // x is number | boolean && typeof x === "number" && (x = 10) // assignment to x && x : number +>(b = x) // x is number | boolean && typeof x === "number" && (x = 10) : number +>(b = x) // x is number | boolean && typeof x === "number" : boolean +>(b = x) : number | boolean +>b = x : number | boolean >b : number | boolean | string ->x : number | string | boolean +>x : number | boolean && typeof x === "number" >typeof x === "number" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string && (x = 10) // assignment to x @@ -336,51 +335,51 @@ function foo11(x: number | string | boolean) { >x : number | string | boolean >10 : number - && x); // x is number | boolean | string ->x : number | string | boolean + && x); // x is number +>x : number } function foo12(x: number | string | boolean) { >foo12 : (x: number | string | boolean) => number >x : number | string | boolean // Mixing typeguards - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression var b: number | boolean | string; >b : number | boolean | string return typeof x === "string" ->typeof x === "string" ? (x = 10 && x.toString().length) // number | boolean | string - changed here : ((b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x) : number +>typeof x === "string" ? ((x = 10) && x.toString().length) // number : ((b = x) // x is number | boolean && typeof x === "number" && x) : number >typeof x === "string" : boolean >typeof x : string >x : number | string | boolean >"string" : string - ? (x = 10 && x.toString().length) // number | boolean | string - changed here ->(x = 10 && x.toString().length) : number ->x = 10 && x.toString().length : number + ? ((x = 10) && x.toString().length) // number +>((x = 10) && x.toString().length) : number +>(x = 10) && x.toString().length : number +>(x = 10) : number +>x = 10 : number >x : number | string | boolean ->10 && x.toString().length : number >10 : number >x.toString().length : number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string | boolean +>x : number >toString : (radix?: number) => string >length : number - : ((b = x) // x is number | boolean | string - changed in true branch ->((b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x) : number ->(b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" && x : number ->(b = x) // x is number | boolean | string - changed in true branch && typeof x === "number" : boolean ->(b = x) : number | string | boolean ->b = x : number | string | boolean + : ((b = x) // x is number | boolean +>((b = x) // x is number | boolean && typeof x === "number" && x) : number +>(b = x) // x is number | boolean && typeof x === "number" && x : number +>(b = x) // x is number | boolean && typeof x === "number" : boolean +>(b = x) : number | boolean +>b = x : number | boolean >b : number | boolean | string ->x : number | string | boolean +>x : number | boolean && typeof x === "number" >typeof x === "number" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string && x); // x is number diff --git a/tests/baselines/reference/typeGuardsInDoStatement.js b/tests/baselines/reference/typeGuardsInDoStatement.js new file mode 100644 index 0000000000000..7d299854f7d25 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInDoStatement.js @@ -0,0 +1,60 @@ +//// [typeGuardsInDoStatement.ts] +let cond: boolean; +function a(x: string | number | boolean) { + x = true; + do { + x; // boolean | string + x = undefined; + } while (typeof x === "string") + x; // number | boolean +} +function b(x: string | number | boolean) { + x = true; + do { + x; // boolean | string + if (cond) continue; + x = undefined; + } while (typeof x === "string") + x; // number | boolean +} +function c(x: string | number) { + x = ""; + do { + x; // string + if (cond) break; + x = undefined; + } while (typeof x === "string") + x; // string | number +} + + +//// [typeGuardsInDoStatement.js] +var cond; +function a(x) { + x = true; + do { + x; // boolean | string + x = undefined; + } while (typeof x === "string"); + x; // number | boolean +} +function b(x) { + x = true; + do { + x; // boolean | string + if (cond) + continue; + x = undefined; + } while (typeof x === "string"); + x; // number | boolean +} +function c(x) { + x = ""; + do { + x; // string + if (cond) + break; + x = undefined; + } while (typeof x === "string"); + x; // string | number +} diff --git a/tests/baselines/reference/typeGuardsInDoStatement.symbols b/tests/baselines/reference/typeGuardsInDoStatement.symbols new file mode 100644 index 0000000000000..7dff18d8d238f --- /dev/null +++ b/tests/baselines/reference/typeGuardsInDoStatement.symbols @@ -0,0 +1,74 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(typeGuardsInDoStatement.ts, 0, 3)) + +function a(x: string | number | boolean) { +>a : Symbol(a, Decl(typeGuardsInDoStatement.ts, 0, 18)) +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) + + x = true; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) + + do { + x; // boolean | string +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) +>undefined : Symbol(undefined) + + } while (typeof x === "string") +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) + + x; // number | boolean +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 1, 11)) +} +function b(x: string | number | boolean) { +>b : Symbol(b, Decl(typeGuardsInDoStatement.ts, 8, 1)) +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) + + x = true; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) + + do { + x; // boolean | string +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) + + if (cond) continue; +>cond : Symbol(cond, Decl(typeGuardsInDoStatement.ts, 0, 3)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) +>undefined : Symbol(undefined) + + } while (typeof x === "string") +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) + + x; // number | boolean +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 9, 11)) +} +function c(x: string | number) { +>c : Symbol(c, Decl(typeGuardsInDoStatement.ts, 17, 1)) +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) + + x = ""; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) + + do { + x; // string +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) + + if (cond) break; +>cond : Symbol(cond, Decl(typeGuardsInDoStatement.ts, 0, 3)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) +>undefined : Symbol(undefined) + + } while (typeof x === "string") +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) + + x; // string | number +>x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) +} + diff --git a/tests/baselines/reference/typeGuardsInDoStatement.types b/tests/baselines/reference/typeGuardsInDoStatement.types new file mode 100644 index 0000000000000..79183e7d6c8d4 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInDoStatement.types @@ -0,0 +1,92 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts === +let cond: boolean; +>cond : boolean + +function a(x: string | number | boolean) { +>a : (x: string | number | boolean) => void +>x : string | number | boolean + + x = true; +>x = true : boolean +>x : string | number | boolean +>true : boolean + + do { + x; // boolean | string +>x : boolean | string + + x = undefined; +>x = undefined : undefined +>x : string | number | boolean +>undefined : undefined + + } while (typeof x === "string") +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean +>"string" : string + + x; // number | boolean +>x : number | boolean +} +function b(x: string | number | boolean) { +>b : (x: string | number | boolean) => void +>x : string | number | boolean + + x = true; +>x = true : boolean +>x : string | number | boolean +>true : boolean + + do { + x; // boolean | string +>x : boolean | string + + if (cond) continue; +>cond : boolean + + x = undefined; +>x = undefined : undefined +>x : string | number | boolean +>undefined : undefined + + } while (typeof x === "string") +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean +>"string" : string + + x; // number | boolean +>x : number | boolean +} +function c(x: string | number) { +>c : (x: string | number) => void +>x : string | number + + x = ""; +>x = "" : string +>x : string | number +>"" : string + + do { + x; // string +>x : string + + if (cond) break; +>cond : boolean + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + + } while (typeof x === "string") +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + x; // string | number +>x : string | number +} + diff --git a/tests/baselines/reference/typeGuardsInForStatement.js b/tests/baselines/reference/typeGuardsInForStatement.js new file mode 100644 index 0000000000000..a2e104bc3f1b4 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInForStatement.js @@ -0,0 +1,48 @@ +//// [typeGuardsInForStatement.ts] +let cond: boolean; +function a(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + } + x; // number +} +function b(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) continue; + } + x; // number +} +function c(x: string | number) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) break; + } + x; // string | number +} + + +//// [typeGuardsInForStatement.js] +var cond; +function a(x) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + } + x; // number +} +function b(x) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) + continue; + } + x; // number +} +function c(x) { + for (x = undefined; typeof x !== "number"; x = undefined) { + x; // string + if (cond) + break; + } + x; // string | number +} diff --git a/tests/baselines/reference/typeGuardsInForStatement.symbols b/tests/baselines/reference/typeGuardsInForStatement.symbols new file mode 100644 index 0000000000000..fe1c10eb14ebb --- /dev/null +++ b/tests/baselines/reference/typeGuardsInForStatement.symbols @@ -0,0 +1,62 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(typeGuardsInForStatement.ts, 0, 3)) + +function a(x: string | number) { +>a : Symbol(a, Decl(typeGuardsInForStatement.ts, 0, 18)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) +>undefined : Symbol(undefined) + + x; // string +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) + } + x; // number +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 1, 11)) +} +function b(x: string | number) { +>b : Symbol(b, Decl(typeGuardsInForStatement.ts, 6, 1)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) +>undefined : Symbol(undefined) + + x; // string +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) + + if (cond) continue; +>cond : Symbol(cond, Decl(typeGuardsInForStatement.ts, 0, 3)) + } + x; // number +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 7, 11)) +} +function c(x: string | number) { +>c : Symbol(c, Decl(typeGuardsInForStatement.ts, 13, 1)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) +>undefined : Symbol(undefined) + + x; // string +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) + + if (cond) break; +>cond : Symbol(cond, Decl(typeGuardsInForStatement.ts, 0, 3)) + } + x; // string | number +>x : Symbol(x, Decl(typeGuardsInForStatement.ts, 14, 11)) +} + diff --git a/tests/baselines/reference/typeGuardsInForStatement.types b/tests/baselines/reference/typeGuardsInForStatement.types new file mode 100644 index 0000000000000..5ebdea53c31d2 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInForStatement.types @@ -0,0 +1,77 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts === +let cond: boolean; +>cond : boolean + +function a(x: string | number) { +>a : (x: string | number) => void +>x : string | number + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x = undefined : undefined +>x : string | number +>undefined : undefined +>typeof x !== "number" : boolean +>typeof x : string +>x : string | number +>"number" : string +>x = undefined : undefined +>x : string | number +>undefined : undefined + + x; // string +>x : string + } + x; // number +>x : number +} +function b(x: string | number) { +>b : (x: string | number) => void +>x : string | number + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x = undefined : undefined +>x : string | number +>undefined : undefined +>typeof x !== "number" : boolean +>typeof x : string +>x : string | number +>"number" : string +>x = undefined : undefined +>x : string | number +>undefined : undefined + + x; // string +>x : string + + if (cond) continue; +>cond : boolean + } + x; // number +>x : number +} +function c(x: string | number) { +>c : (x: string | number) => void +>x : string | number + + for (x = undefined; typeof x !== "number"; x = undefined) { +>x = undefined : undefined +>x : string | number +>undefined : undefined +>typeof x !== "number" : boolean +>typeof x : string +>x : string | number +>"number" : string +>x = undefined : undefined +>x : string | number +>undefined : undefined + + x; // string +>x : string + + if (cond) break; +>cond : boolean + } + x; // string | number +>x : number | string +} + diff --git a/tests/baselines/reference/typeGuardsInIfStatement.errors.txt b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt new file mode 100644 index 0000000000000..984ca454e76af --- /dev/null +++ b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt @@ -0,0 +1,153 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(22,10): error TS2354: No best common type exists among return expressions. +tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(31,10): error TS2354: No best common type exists among return expressions. +tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(49,10): error TS2354: No best common type exists among return expressions. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts (3 errors) ==== + // In the true branch statement of an 'if' statement, + // the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. + // In the false branch statement of an 'if' statement, + // the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false. + function foo(x: number | string) { + if (typeof x === "string") { + return x.length; // string + } + else { + return x++; // number + } + } + function foo2(x: number | string) { + if (typeof x === "string") { + x = 10; + return x; // number + } + else { + return x; // number + } + } + function foo3(x: number | string) { + ~~~~ +!!! error TS2354: No best common type exists among return expressions. + if (typeof x === "string") { + x = "Hello"; + return x; // string + } + else { + return x; // number + } + } + function foo4(x: number | string) { + ~~~~ +!!! error TS2354: No best common type exists among return expressions. + if (typeof x === "string") { + return x; // string + } + else { + x = 10; + return x; // number + } + } + function foo5(x: number | string) { + if (typeof x === "string") { + return x; // string + } + else { + x = "hello"; + return x; // string + } + } + function foo6(x: number | string) { + ~~~~ +!!! error TS2354: No best common type exists among return expressions. + if (typeof x === "string") { + x = 10; + return x; // number + } + else { + x = "hello"; + return x; // string + } + } + function foo7(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } + } + function foo8(x: number | string | boolean) { + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var b: number | boolean = x; // number | boolean + if (typeof x === "boolean") { + return x; // boolean + } + else { + return x == 10; // number + } + } + } + function foo9(x: number | string) { + var y = 10; + if (typeof x === "string") { + // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop + y = x.length; + return x === "hello"; // string + } + else { + return x == 10; // number + } + } + function foo10(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + if (typeof x === "string") { + return x === "hello"; // string + } + else { + var y: boolean | string; + var b = x; // number | boolean + return typeof x === "number" + ? x === 10 // number + : x; // x should be boolean + } + } + function foo11(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x deep inside another guard stops narrowing of type too + if (typeof x === "string") { + return x; // string | number | boolean - x changed in else branch + } + else { + var y: number| boolean | string; + var b = x; // number | boolean | string - because below we are changing value of x in if statement + return typeof x === "number" + ? ( + // change value of x + x = 10 && x.toString() // number | boolean | string + ) + : ( + // do not change value + y = x && x.toString() // number | boolean | string + ); + } + } + function foo12(x: number | string | boolean) { + // Mixing typeguard narrowing in if statement with conditional expression typeguard + // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression + if (typeof x === "string") { + return x.toString(); // string | number | boolean - x changed in else branch + } + else { + x = 10; + var b = x; // number | boolean | string + return typeof x === "number" + ? x.toString() // number + : x.toString(); // boolean | string + } + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsInIfStatement.js b/tests/baselines/reference/typeGuardsInIfStatement.js index de05a7bf82af3..8e841d2eac3ef 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.js +++ b/tests/baselines/reference/typeGuardsInIfStatement.js @@ -1,10 +1,8 @@ //// [typeGuardsInIfStatement.ts] // In the true branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true, -// provided the true branch statement contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. // In the false branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false, -// provided the false branch statement contains no assignments to the variable or parameter +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false. function foo(x: number | string) { if (typeof x === "string") { return x.length; // string @@ -14,54 +12,49 @@ function foo(x: number | string) { } } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { - return x; // string | number + return x; // number } } function foo3(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x; // string } else { - return x; // string | number + return x; // number } } function foo4(x: number | string) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { - x = 10; // even though assigned number - this should result in x to be string | number - return x; // string | number + x = 10; + return x; // number } } function foo5(x: number | string) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { x = "hello"; - return x; // string | number + return x; // string } } function foo6(x: number | string) { - // Modify in both branches if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { x = "hello"; - return x; // string | number + return x; // string } } function foo7(x: number | string | boolean) { @@ -150,11 +143,9 @@ function foo12(x: number | string | boolean) { //// [typeGuardsInIfStatement.js] // In the true branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true, -// provided the true branch statement contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. // In the false branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false, -// provided the false branch statement contains no assignments to the variable or parameter +// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false. function foo(x) { if (typeof x === "string") { return x.length; // string @@ -164,54 +155,49 @@ function foo(x) { } } function foo2(x) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { - return x; // string | number + return x; // number } } function foo3(x) { - // x is assigned in the if true branch, the type is not narrowed if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x; // string } else { - return x; // string | number + return x; // number } } function foo4(x) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { - x = 10; // even though assigned number - this should result in x to be string | number - return x; // string | number + x = 10; + return x; // number } } function foo5(x) { - // false branch updates the variable - so here it is not number if (typeof x === "string") { - return x; // string | number + return x; // string } else { x = "hello"; - return x; // string | number + return x; // string } } function foo6(x) { - // Modify in both branches if (typeof x === "string") { x = 10; - return x; // string | number + return x; // number } else { x = "hello"; - return x; // string | number + return x; // string } } function foo7(x) { diff --git a/tests/baselines/reference/typeGuardsInIfStatement.symbols b/tests/baselines/reference/typeGuardsInIfStatement.symbols deleted file mode 100644 index 54a65f0693b47..0000000000000 --- a/tests/baselines/reference/typeGuardsInIfStatement.symbols +++ /dev/null @@ -1,304 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts === -// In the true branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true, -// provided the true branch statement contains no assignments to the variable or parameter. -// In the false branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false, -// provided the false branch statement contains no assignments to the variable or parameter -function foo(x: number | string) { ->foo : Symbol(foo, Decl(typeGuardsInIfStatement.ts, 0, 0)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) - - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) - - return x.length; // string ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) - } - else { - return x++; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) - } -} -function foo2(x: number | string) { ->foo2 : Symbol(foo2, Decl(typeGuardsInIfStatement.ts, 13, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) - - // x is assigned in the if true branch, the type is not narrowed - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) - - x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) - } - else { - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) - } -} -function foo3(x: number | string) { ->foo3 : Symbol(foo3, Decl(typeGuardsInIfStatement.ts, 23, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) - - // x is assigned in the if true branch, the type is not narrowed - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) - - x = "Hello"; // even though assigned using same type as narrowed expression ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) - } - else { - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) - } -} -function foo4(x: number | string) { ->foo4 : Symbol(foo4, Decl(typeGuardsInIfStatement.ts, 33, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) - - // false branch updates the variable - so here it is not number - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) - } - else { - x = 10; // even though assigned number - this should result in x to be string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) - } -} -function foo5(x: number | string) { ->foo5 : Symbol(foo5, Decl(typeGuardsInIfStatement.ts, 43, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) - - // false branch updates the variable - so here it is not number - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) - } - else { - x = "hello"; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) - } -} -function foo6(x: number | string) { ->foo6 : Symbol(foo6, Decl(typeGuardsInIfStatement.ts, 53, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - - // Modify in both branches - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - - x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - } - else { - x = "hello"; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) - } -} -function foo7(x: number | string | boolean) { ->foo7 : Symbol(foo7, Decl(typeGuardsInIfStatement.ts, 64, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - - return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - } - else if (typeof x === "boolean") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - - return x; // boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - } - else { - return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) - } -} -function foo8(x: number | string | boolean) { ->foo8 : Symbol(foo8, Decl(typeGuardsInIfStatement.ts, 75, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - - return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - } - else { - var b: number | boolean = x; // number | boolean ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 81, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - - if (typeof x === "boolean") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - - return x; // boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - } - else { - return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) - } - } -} -function foo9(x: number | string) { ->foo9 : Symbol(foo9, Decl(typeGuardsInIfStatement.ts, 89, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) - - var y = 10; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 91, 7)) - - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) - - // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop - y = x.length; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 91, 7)) ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) - - return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) - } - else { - return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) - } -} -function foo10(x: number | string | boolean) { ->foo10 : Symbol(foo10, Decl(typeGuardsInIfStatement.ts, 100, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - - return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - } - else { - var y: boolean | string; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 107, 11)) - - var b = x; // number | boolean ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 108, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - - return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - - ? x === 10 // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - - : x; // x should be boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) - } -} -function foo11(x: number | string | boolean) { ->foo11 : Symbol(foo11, Decl(typeGuardsInIfStatement.ts, 113, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - // Assigning value to x deep inside another guard stops narrowing of type too - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) - - return x; // string | number | boolean - x changed in else branch ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) - } - else { - var y: number| boolean | string; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 121, 11)) - - var b = x; // number | boolean | string - because below we are changing value of x in if statement ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 122, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) - - return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) - - ? ( - // change value of x - x = 10 && x.toString() // number | boolean | string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - - ) - : ( - // do not change value - y = x && x.toString() // number | boolean | string ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 121, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - - ); - } -} -function foo12(x: number | string | boolean) { ->foo12 : Symbol(foo12, Decl(typeGuardsInIfStatement.ts, 133, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) - - return x.toString(); // string | number | boolean - x changed in else branch ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - } - else { - x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) - - var b = x; // number | boolean | string ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 142, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) - - return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) - - ? x.toString() // number ->x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) ->toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) - - : x.toString(); // boolean | string ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - } -} diff --git a/tests/baselines/reference/typeGuardsInIfStatement.types b/tests/baselines/reference/typeGuardsInIfStatement.types deleted file mode 100644 index 0095a7cc768ea..0000000000000 --- a/tests/baselines/reference/typeGuardsInIfStatement.types +++ /dev/null @@ -1,405 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts === -// In the true branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true, -// provided the true branch statement contains no assignments to the variable or parameter. -// In the false branch statement of an 'if' statement, -// the type of a variable or parameter is narrowed by any type guard in the 'if' condition when false, -// provided the false branch statement contains no assignments to the variable or parameter -function foo(x: number | string) { ->foo : (x: number | string) => number ->x : number | string - - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - return x.length; // string ->x.length : number ->x : string ->length : number - } - else { - return x++; // number ->x++ : number ->x : number - } -} -function foo2(x: number | string) { ->foo2 : (x: number | string) => number | string ->x : number | string - - // x is assigned in the if true branch, the type is not narrowed - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - x = 10; ->x = 10 : number ->x : number | string ->10 : number - - return x; // string | number ->x : number | string - } - else { - return x; // string | number ->x : number | string - } -} -function foo3(x: number | string) { ->foo3 : (x: number | string) => number | string ->x : number | string - - // x is assigned in the if true branch, the type is not narrowed - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - x = "Hello"; // even though assigned using same type as narrowed expression ->x = "Hello" : string ->x : number | string ->"Hello" : string - - return x; // string | number ->x : number | string - } - else { - return x; // string | number ->x : number | string - } -} -function foo4(x: number | string) { ->foo4 : (x: number | string) => number | string ->x : number | string - - // false branch updates the variable - so here it is not number - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - return x; // string | number ->x : number | string - } - else { - x = 10; // even though assigned number - this should result in x to be string | number ->x = 10 : number ->x : number | string ->10 : number - - return x; // string | number ->x : number | string - } -} -function foo5(x: number | string) { ->foo5 : (x: number | string) => number | string ->x : number | string - - // false branch updates the variable - so here it is not number - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - return x; // string | number ->x : number | string - } - else { - x = "hello"; ->x = "hello" : string ->x : number | string ->"hello" : string - - return x; // string | number ->x : number | string - } -} -function foo6(x: number | string) { ->foo6 : (x: number | string) => number | string ->x : number | string - - // Modify in both branches - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - x = 10; ->x = 10 : number ->x : number | string ->10 : number - - return x; // string | number ->x : number | string - } - else { - x = "hello"; ->x = "hello" : string ->x : number | string ->"hello" : string - - return x; // string | number ->x : number | string - } -} -function foo7(x: number | string | boolean) { ->foo7 : (x: number | string | boolean) => boolean ->x : number | string | boolean - - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string | boolean ->"string" : string - - return x === "hello"; // string ->x === "hello" : boolean ->x : string ->"hello" : string - } - else if (typeof x === "boolean") { ->typeof x === "boolean" : boolean ->typeof x : string ->x : number | boolean ->"boolean" : string - - return x; // boolean ->x : boolean - } - else { - return x == 10; // number ->x == 10 : boolean ->x : number ->10 : number - } -} -function foo8(x: number | string | boolean) { ->foo8 : (x: number | string | boolean) => boolean ->x : number | string | boolean - - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string | boolean ->"string" : string - - return x === "hello"; // string ->x === "hello" : boolean ->x : string ->"hello" : string - } - else { - var b: number | boolean = x; // number | boolean ->b : number | boolean ->x : number | boolean - - if (typeof x === "boolean") { ->typeof x === "boolean" : boolean ->typeof x : string ->x : number | boolean ->"boolean" : string - - return x; // boolean ->x : boolean - } - else { - return x == 10; // number ->x == 10 : boolean ->x : number ->10 : number - } - } -} -function foo9(x: number | string) { ->foo9 : (x: number | string) => boolean ->x : number | string - - var y = 10; ->y : number ->10 : number - - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - - // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop - y = x.length; ->y = x.length : number ->y : number ->x.length : number ->x : string ->length : number - - return x === "hello"; // string ->x === "hello" : boolean ->x : string ->"hello" : string - } - else { - return x == 10; // number ->x == 10 : boolean ->x : number ->10 : number - } -} -function foo10(x: number | string | boolean) { ->foo10 : (x: number | string | boolean) => boolean ->x : number | string | boolean - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string | boolean ->"string" : string - - return x === "hello"; // string ->x === "hello" : boolean ->x : string ->"hello" : string - } - else { - var y: boolean | string; ->y : boolean | string - - var b = x; // number | boolean ->b : number | boolean ->x : number | boolean - - return typeof x === "number" ->typeof x === "number" ? x === 10 // number : x : boolean ->typeof x === "number" : boolean ->typeof x : string ->x : number | boolean ->"number" : string - - ? x === 10 // number ->x === 10 : boolean ->x : number ->10 : number - - : x; // x should be boolean ->x : boolean - } -} -function foo11(x: number | string | boolean) { ->foo11 : (x: number | string | boolean) => number | string | boolean ->x : number | string | boolean - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - // Assigning value to x deep inside another guard stops narrowing of type too - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string | boolean ->"string" : string - - return x; // string | number | boolean - x changed in else branch ->x : number | string | boolean - } - else { - var y: number| boolean | string; ->y : number | boolean | string - - var b = x; // number | boolean | string - because below we are changing value of x in if statement ->b : number | string | boolean ->x : number | string | boolean - - return typeof x === "number" ->typeof x === "number" ? ( // change value of x x = 10 && x.toString() // number | boolean | string ) : ( // do not change value y = x && x.toString() // number | boolean | string ) : string ->typeof x === "number" : boolean ->typeof x : string ->x : number | string | boolean ->"number" : string - - ? ( ->( // change value of x x = 10 && x.toString() // number | boolean | string ) : string - - // change value of x - x = 10 && x.toString() // number | boolean | string ->x = 10 && x.toString() : string ->x : number | string | boolean ->10 && x.toString() : string ->10 : number ->x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string - - ) - : ( ->( // do not change value y = x && x.toString() // number | boolean | string ) : string - - // do not change value - y = x && x.toString() // number | boolean | string ->y = x && x.toString() : string ->y : number | boolean | string ->x && x.toString() : string ->x : number | string | boolean ->x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string - - ); - } -} -function foo12(x: number | string | boolean) { ->foo12 : (x: number | string | boolean) => string ->x : number | string | boolean - - // Mixing typeguard narrowing in if statement with conditional expression typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - if (typeof x === "string") { ->typeof x === "string" : boolean ->typeof x : string ->x : number | string | boolean ->"string" : string - - return x.toString(); // string | number | boolean - x changed in else branch ->x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string - } - else { - x = 10; ->x = 10 : number ->x : number | string | boolean ->10 : number - - var b = x; // number | boolean | string ->b : number | string | boolean ->x : number | string | boolean - - return typeof x === "number" ->typeof x === "number" ? x.toString() // number : x.toString() : string ->typeof x === "number" : boolean ->typeof x : string ->x : number | string | boolean ->"number" : string - - ? x.toString() // number ->x.toString() : string ->x.toString : (radix?: number) => string ->x : number ->toString : (radix?: number) => string - - : x.toString(); // boolean | string ->x.toString() : string ->x.toString : () => string ->x : string | boolean ->toString : () => string - } -} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js index e3f1a8c72c298..be1c497e3ea0d 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.js @@ -1,7 +1,6 @@ //// [typeGuardsInRightOperandOfAndAndOperator.ts] // In the right operand of a && operation, -// the type of a variable or parameter is narrowed by any type guard in the left operand when true, -// provided the right operand contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the left operand when true. function foo(x: number | string) { return typeof x === "string" && x.length === 10; // string } @@ -36,29 +35,19 @@ function foo7(x: number | string | boolean) { var y: number| boolean | string; var z: number| boolean | string; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x !== "string" - && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && ((z = x) // number | boolean && (typeof x === "number" // change value of x - ? (x = 10 && x.toString()) // number | boolean | string + ? ((x = 10) && x.toString()) // x is number // do not change value - : (y = x && x.toString()))); // number | boolean | string + : ((y = x) && x.toString()))); // x is boolean } -function foo8(x: number | string) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" - && (x = 10) // change x - number| string - && (typeof x === "number" - ? x // number - : x.length); // string -} + //// [typeGuardsInRightOperandOfAndAndOperator.js] // In the right operand of a && operation, -// the type of a variable or parameter is narrowed by any type guard in the left operand when true, -// provided the right operand contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the left operand when true. function foo(x) { return typeof x === "string" && x.length === 10; // string } @@ -93,19 +82,9 @@ function foo7(x) { var y; var z; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x !== "string" - && ((z = x) // string | number | boolean - x changed deeper in conditional expression + && ((z = x) // number | boolean && (typeof x === "number" - ? (x = 10 && x.toString()) // number | boolean | string - : (y = x && x.toString()))); // number | boolean | string -} -function foo8(x) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" - && (x = 10) // change x - number| string - && (typeof x === "number" - ? x // number - : x.length); // string + ? ((x = 10) && x.toString()) // x is number + : ((y = x) && x.toString()))); // x is boolean } diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols index 21e3dd3701f44..4028edb6b429a 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols @@ -1,143 +1,119 @@ === tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts === // In the right operand of a && operation, -// the type of a variable or parameter is narrowed by any type guard in the left operand when true, -// provided the right operand contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the left operand when true. function foo(x: number | string) { >foo : Symbol(foo, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 0, 0)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 3, 13)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 2, 13)) return typeof x === "string" && x.length === 10; // string ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 3, 13)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 2, 13)) >x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 3, 13)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 2, 13)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) } function foo2(x: number | string) { ->foo2 : Symbol(foo2, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 5, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 6, 14)) +>foo2 : Symbol(foo2, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 4, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 5, 14)) // modify x in right hand operand return typeof x === "string" && ((x = 10) && x); // string | number ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 6, 14)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 6, 14)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 6, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 5, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 5, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 5, 14)) } function foo3(x: number | string) { ->foo3 : Symbol(foo3, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 9, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 10, 14)) +>foo3 : Symbol(foo3, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 8, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 9, 14)) // modify x in right hand operand with string type itself return typeof x === "string" && ((x = "hello") && x); // string | number ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 10, 14)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 10, 14)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 10, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 9, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 9, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 9, 14)) } function foo4(x: number | string | boolean) { ->foo4 : Symbol(foo4, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 13, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 14, 14)) +>foo4 : Symbol(foo4, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 12, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 13, 14)) return typeof x !== "string" // string | number | boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 13, 14)) && typeof x !== "number" // number | boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 13, 14)) && x; // boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 13, 14)) } function foo5(x: number | string | boolean) { ->foo5 : Symbol(foo5, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 19, 14)) +>foo5 : Symbol(foo5, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 17, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 14)) // usage of x or assignment to separate variable shouldn't cause narrowing of type to stop var b: number | boolean; ->b : Symbol(b, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 21, 7)) +>b : Symbol(b, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 20, 7)) return typeof x !== "string" // string | number | boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 19, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 14)) && ((b = x) && (typeof x !== "number" // number | boolean ->b : Symbol(b, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 21, 7)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 19, 14)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 19, 14)) +>b : Symbol(b, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 20, 7)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 14)) && x)); // boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 19, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 18, 14)) } function foo6(x: number | string | boolean) { ->foo6 : Symbol(foo6, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 26, 14)) +>foo6 : Symbol(foo6, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 24, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 14)) // Mixing typeguard narrowing in if statement with conditional expression typeguard return typeof x !== "string" // string | number | boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 26, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 14)) && (typeof x !== "number" // number | boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 26, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 14)) ? x // boolean ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 26, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 14)) : x === 10) // number ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 26, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 25, 14)) } function foo7(x: number | string | boolean) { ->foo7 : Symbol(foo7, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) +>foo7 : Symbol(foo7, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 31, 1)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) var y: number| boolean | string; ->y : Symbol(y, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 34, 7)) +>y : Symbol(y, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 7)) var z: number| boolean | string; ->z : Symbol(z, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 35, 7)) +>z : Symbol(z, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 34, 7)) // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x !== "string" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) - && ((z = x) // string | number | boolean - x changed deeper in conditional expression ->z : Symbol(z, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 35, 7)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) + && ((z = x) // number | boolean +>z : Symbol(z, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 34, 7)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) && (typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) // change value of x - ? (x = 10 && x.toString()) // number | boolean | string ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + ? ((x = 10) && x.toString()) // x is number +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) // do not change value - : (y = x && x.toString()))); // number | boolean | string ->y : Symbol(y, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 34, 7)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + : ((y = x) && x.toString()))); // x is boolean +>y : Symbol(y, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 33, 7)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) +>x.toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 32, 14)) +>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) } -function foo8(x: number | string) { ->foo8 : Symbol(foo8, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 45, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) - - && (x = 10) // change x - number| string ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) - - && (typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) - - ? x // number ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) - - : x.length); // string ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 46, 14)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) -} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types index 22d390618c165..2da52b8b1b8c7 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types @@ -1,7 +1,6 @@ === tests/cases/conformance/expressions/typeGuards/typeGuardsInRightOperandOfAndAndOperator.ts === // In the right operand of a && operation, -// the type of a variable or parameter is narrowed by any type guard in the left operand when true, -// provided the right operand contains no assignments to the variable or parameter. +// the type of a variable or parameter is narrowed by any type guard in the left operand when true. function foo(x: number | string) { >foo : (x: number | string) => boolean >x : number | string @@ -19,42 +18,42 @@ function foo(x: number | string) { >10 : number } function foo2(x: number | string) { ->foo2 : (x: number | string) => number | string +>foo2 : (x: number | string) => number >x : number | string // modify x in right hand operand return typeof x === "string" && ((x = 10) && x); // string | number ->typeof x === "string" && ((x = 10) && x) : number | string +>typeof x === "string" && ((x = 10) && x) : number >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string ->((x = 10) && x) : number | string ->(x = 10) && x : number | string +>((x = 10) && x) : number +>(x = 10) && x : number >(x = 10) : number >x = 10 : number >x : number | string >10 : number ->x : number | string +>x : number } function foo3(x: number | string) { ->foo3 : (x: number | string) => number | string +>foo3 : (x: number | string) => string >x : number | string // modify x in right hand operand with string type itself return typeof x === "string" && ((x = "hello") && x); // string | number ->typeof x === "string" && ((x = "hello") && x) : number | string +>typeof x === "string" && ((x = "hello") && x) : string >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string ->((x = "hello") && x) : number | string ->(x = "hello") && x : number | string +>((x = "hello") && x) : string +>(x = "hello") && x : string >(x = "hello") : string >x = "hello" : string >x : number | string >"hello" : string ->x : number | string +>x : string } function foo4(x: number | string | boolean) { >foo4 : (x: number | string | boolean) => boolean @@ -148,87 +147,53 @@ function foo7(x: number | string | boolean) { >z : number | boolean | string // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x !== "string" ->typeof x !== "string" && ((z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string +>typeof x !== "string" && ((z = x) // number | boolean && (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString()))) : string >typeof x !== "string" : boolean >typeof x : string >x : number | string | boolean >"string" : string - && ((z = x) // string | number | boolean - x changed deeper in conditional expression ->((z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : string ->(z = x) // string | number | boolean - x changed deeper in conditional expression && (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string ->(z = x) : number | string | boolean ->z = x : number | string | boolean + && ((z = x) // number | boolean +>((z = x) // number | boolean && (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString()))) : string +>(z = x) // number | boolean && (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString())) : string +>(z = x) : number | boolean +>z = x : number | boolean >z : number | boolean | string ->x : number | string | boolean +>x : number | boolean && (typeof x === "number" ->(typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string ->typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()) : string +>(typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString())) : string +>typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString()) : string >typeof x === "number" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string // change value of x - ? (x = 10 && x.toString()) // number | boolean | string ->(x = 10 && x.toString()) : string ->x = 10 && x.toString() : string + ? ((x = 10) && x.toString()) // x is number +>((x = 10) && x.toString()) : string +>(x = 10) && x.toString() : string +>(x = 10) : number +>x = 10 : number >x : number | string | boolean ->10 && x.toString() : string >10 : number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string | boolean +>x : number >toString : (radix?: number) => string // do not change value - : (y = x && x.toString()))); // number | boolean | string ->(y = x && x.toString()) : string ->y = x && x.toString() : string + : ((y = x) && x.toString()))); // x is boolean +>((y = x) && x.toString()) : string +>(y = x) && x.toString() : string +>(y = x) : boolean +>y = x : boolean >y : number | boolean | string ->x && x.toString() : string ->x : number | string | boolean +>x : boolean >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : boolean +>toString : () => string } -function foo8(x: number | string) { ->foo8 : (x: number | string) => number ->x : number | string - - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x !== "string" ->typeof x !== "string" && (x = 10) // change x - number| string && (typeof x === "number" ? x // number : x.length) : number ->typeof x !== "string" && (x = 10) : number ->typeof x !== "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - && (x = 10) // change x - number| string ->(x = 10) : number ->x = 10 : number ->x : number | string ->10 : number - - && (typeof x === "number" ->(typeof x === "number" ? x // number : x.length) : number ->typeof x === "number" ? x // number : x.length : number ->typeof x === "number" : boolean ->typeof x : string ->x : number | string ->"number" : string - - ? x // number ->x : number - - : x.length); // string ->x.length : number ->x : string ->length : number -} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js index 188d226da1390..a8d66dc5fa22c 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.js @@ -36,24 +36,15 @@ function foo7(x: number | string | boolean) { var y: number| boolean | string; var z: number| boolean | string; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x === "string" - || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || ((z = x) // number | boolean || (typeof x === "number" // change value of x - ? (x = 10 && x.toString()) // number | boolean | string + ? ((x = 10) && x.toString()) // number | boolean | string // do not change value - : (y = x && x.toString()))); // number | boolean | string + : ((y = x) && x.toString()))); // number | boolean | string } -function foo8(x: number | string) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" - || (x = 10) // change x - number| string - || (typeof x === "number" - ? x // number - : x.length); // string -} + //// [typeGuardsInRightOperandOfOrOrOperator.js] // In the right operand of a || operation, @@ -93,19 +84,9 @@ function foo7(x) { var y; var z; // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x === "string" - || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || ((z = x) // number | boolean || (typeof x === "number" - ? (x = 10 && x.toString()) // number | boolean | string - : (y = x && x.toString()))); // number | boolean | string -} -function foo8(x) { - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" - || (x = 10) // change x - number| string - || (typeof x === "number" - ? x // number - : x.length); // string + ? ((x = 10) && x.toString()) // number | boolean | string + : ((y = x) && x.toString()))); // number | boolean | string } diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols index de9b4396d3d4a..b276671819a76 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols @@ -92,11 +92,10 @@ function foo7(x: number | string | boolean) { >z : Symbol(z, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 35, 7)) // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x === "string" >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) - || ((z = x) // string | number | boolean - x changed deeper in conditional expression + || ((z = x) // number | boolean >z : Symbol(z, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 35, 7)) >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) @@ -104,40 +103,18 @@ function foo7(x: number | string | boolean) { >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) // change value of x - ? (x = 10 && x.toString()) // number | boolean | string + ? ((x = 10) && x.toString()) // number | boolean | string >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) // do not change value - : (y = x && x.toString()))); // number | boolean | string + : ((y = x) && x.toString()))); // number | boolean | string >y : Symbol(y, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 34, 7)) >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 33, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) } -function foo8(x: number | string) { ->foo8 : Symbol(foo8, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 45, 1)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) - - || (x = 10) // change x - number| string ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) - - || (typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) - - ? x // number ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) - - : x.length); // string ->x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInRightOperandOfOrOrOperator.ts, 46, 14)) ->length : Symbol(String.length, Decl(lib.d.ts, --, --)) -} diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types index 38d9a723d3a86..659182ab888cf 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types @@ -19,42 +19,42 @@ function foo(x: number | string) { >10 : number } function foo2(x: number | string) { ->foo2 : (x: number | string) => boolean | number | string +>foo2 : (x: number | string) => boolean | number >x : number | string // modify x in right hand operand return typeof x !== "string" || ((x = 10) || x); // string | number ->typeof x !== "string" || ((x = 10) || x) : boolean | number | string +>typeof x !== "string" || ((x = 10) || x) : boolean | number >typeof x !== "string" : boolean >typeof x : string >x : number | string >"string" : string ->((x = 10) || x) : number | string ->(x = 10) || x : number | string +>((x = 10) || x) : number +>(x = 10) || x : number >(x = 10) : number >x = 10 : number >x : number | string >10 : number ->x : number | string +>x : number } function foo3(x: number | string) { ->foo3 : (x: number | string) => boolean | string | number +>foo3 : (x: number | string) => boolean | string >x : number | string // modify x in right hand operand with string type itself return typeof x !== "string" || ((x = "hello") || x); // string | number ->typeof x !== "string" || ((x = "hello") || x) : boolean | string | number +>typeof x !== "string" || ((x = "hello") || x) : boolean | string >typeof x !== "string" : boolean >typeof x : string >x : number | string >"string" : string ->((x = "hello") || x) : string | number ->(x = "hello") || x : string | number +>((x = "hello") || x) : string +>(x = "hello") || x : string >(x = "hello") : string >x = "hello" : string >x : number | string >"hello" : string ->x : number | string +>x : string } function foo4(x: number | string | boolean) { >foo4 : (x: number | string | boolean) => boolean @@ -148,87 +148,53 @@ function foo7(x: number | string | boolean) { >z : number | boolean | string // Mixing typeguard narrowing - // Assigning value to x deep inside another guard stops narrowing of type too return typeof x === "string" ->typeof x === "string" || ((z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : boolean | number | string +>typeof x === "string" || ((z = x) // number | boolean || (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // number | boolean | string // do not change value : ((y = x) && x.toString()))) : boolean | number | string >typeof x === "string" : boolean >typeof x : string >x : number | string | boolean >"string" : string - || ((z = x) // string | number | boolean - x changed deeper in conditional expression ->((z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()))) : number | string | boolean ->(z = x) // string | number | boolean - x changed deeper in conditional expression || (typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : number | string | boolean ->(z = x) : number | string | boolean ->z = x : number | string | boolean + || ((z = x) // number | boolean +>((z = x) // number | boolean || (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // number | boolean | string // do not change value : ((y = x) && x.toString()))) : number | boolean | string +>(z = x) // number | boolean || (typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // number | boolean | string // do not change value : ((y = x) && x.toString())) : number | boolean | string +>(z = x) : number | boolean +>z = x : number | boolean >z : number | boolean | string ->x : number | string | boolean +>x : number | boolean || (typeof x === "number" ->(typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString())) : string ->typeof x === "number" // change value of x ? (x = 10 && x.toString()) // number | boolean | string // do not change value : (y = x && x.toString()) : string +>(typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // number | boolean | string // do not change value : ((y = x) && x.toString())) : string +>typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // number | boolean | string // do not change value : ((y = x) && x.toString()) : string >typeof x === "number" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string // change value of x - ? (x = 10 && x.toString()) // number | boolean | string ->(x = 10 && x.toString()) : string ->x = 10 && x.toString() : string + ? ((x = 10) && x.toString()) // number | boolean | string +>((x = 10) && x.toString()) : string +>(x = 10) && x.toString() : string +>(x = 10) : number +>x = 10 : number >x : number | string | boolean ->10 && x.toString() : string >10 : number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string | boolean +>x : number >toString : (radix?: number) => string // do not change value - : (y = x && x.toString()))); // number | boolean | string ->(y = x && x.toString()) : string ->y = x && x.toString() : string + : ((y = x) && x.toString()))); // number | boolean | string +>((y = x) && x.toString()) : string +>(y = x) && x.toString() : string +>(y = x) : boolean +>y = x : boolean >y : number | boolean | string ->x && x.toString() : string ->x : number | string | boolean +>x : boolean >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : boolean +>toString : () => string } -function foo8(x: number | string) { ->foo8 : (x: number | string) => boolean | number ->x : number | string - - // Mixing typeguard - // Assigning value to x in outer guard shouldn't stop narrowing in the inner expression - return typeof x === "string" ->typeof x === "string" || (x = 10) // change x - number| string || (typeof x === "number" ? x // number : x.length) : boolean | number ->typeof x === "string" || (x = 10) : boolean | number ->typeof x === "string" : boolean ->typeof x : string ->x : number | string ->"string" : string - || (x = 10) // change x - number| string ->(x = 10) : number ->x = 10 : number ->x : number | string ->10 : number - - || (typeof x === "number" ->(typeof x === "number" ? x // number : x.length) : number ->typeof x === "number" ? x // number : x.length : number ->typeof x === "number" : boolean ->typeof x : string ->x : number | string ->"number" : string - - ? x // number ->x : number - - : x.length); // string ->x.length : number ->x : string ->length : number -} diff --git a/tests/baselines/reference/typeGuardsInWhileStatement.js b/tests/baselines/reference/typeGuardsInWhileStatement.js new file mode 100644 index 0000000000000..ac2ceaaf590ba --- /dev/null +++ b/tests/baselines/reference/typeGuardsInWhileStatement.js @@ -0,0 +1,54 @@ +//// [typeGuardsInWhileStatement.ts] +let cond: boolean; +function a(x: string | number) { + while (typeof x === "string") { + x; // string + x = undefined; + } + x; // number +} +function b(x: string | number) { + while (typeof x === "string") { + if (cond) continue; + x; // string + x = undefined; + } + x; // number +} +function c(x: string | number) { + while (typeof x === "string") { + if (cond) break; + x; // string + x = undefined; + } + x; // string | number +} + + +//// [typeGuardsInWhileStatement.js] +var cond; +function a(x) { + while (typeof x === "string") { + x; // string + x = undefined; + } + x; // number +} +function b(x) { + while (typeof x === "string") { + if (cond) + continue; + x; // string + x = undefined; + } + x; // number +} +function c(x) { + while (typeof x === "string") { + if (cond) + break; + x; // string + x = undefined; + } + x; // string | number +} diff --git a/tests/baselines/reference/typeGuardsInWhileStatement.symbols b/tests/baselines/reference/typeGuardsInWhileStatement.symbols new file mode 100644 index 0000000000000..b981943e216a2 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInWhileStatement.symbols @@ -0,0 +1,62 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(typeGuardsInWhileStatement.ts, 0, 3)) + +function a(x: string | number) { +>a : Symbol(a, Decl(typeGuardsInWhileStatement.ts, 0, 18)) +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 1, 11)) + + while (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 1, 11)) + + x; // string +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 1, 11)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 1, 11)) +>undefined : Symbol(undefined) + } + x; // number +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 1, 11)) +} +function b(x: string | number) { +>b : Symbol(b, Decl(typeGuardsInWhileStatement.ts, 7, 1)) +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 8, 11)) + + while (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 8, 11)) + + if (cond) continue; +>cond : Symbol(cond, Decl(typeGuardsInWhileStatement.ts, 0, 3)) + + x; // string +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 8, 11)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 8, 11)) +>undefined : Symbol(undefined) + } + x; // number +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 8, 11)) +} +function c(x: string | number) { +>c : Symbol(c, Decl(typeGuardsInWhileStatement.ts, 15, 1)) +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 16, 11)) + + while (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 16, 11)) + + if (cond) break; +>cond : Symbol(cond, Decl(typeGuardsInWhileStatement.ts, 0, 3)) + + x; // string +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 16, 11)) + + x = undefined; +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 16, 11)) +>undefined : Symbol(undefined) + } + x; // string | number +>x : Symbol(x, Decl(typeGuardsInWhileStatement.ts, 16, 11)) +} + diff --git a/tests/baselines/reference/typeGuardsInWhileStatement.types b/tests/baselines/reference/typeGuardsInWhileStatement.types new file mode 100644 index 0000000000000..cde045cc621c3 --- /dev/null +++ b/tests/baselines/reference/typeGuardsInWhileStatement.types @@ -0,0 +1,74 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts === +let cond: boolean; +>cond : boolean + +function a(x: string | number) { +>a : (x: string | number) => void +>x : string | number + + while (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + x; // string +>x : string + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + } + x; // number +>x : number +} +function b(x: string | number) { +>b : (x: string | number) => void +>x : string | number + + while (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + if (cond) continue; +>cond : boolean + + x; // string +>x : string + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + } + x; // number +>x : number +} +function c(x: string | number) { +>c : (x: string | number) => void +>x : string | number + + while (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + if (cond) break; +>cond : boolean + + x; // string +>x : string + + x = undefined; +>x = undefined : undefined +>x : string | number +>undefined : undefined + } + x; // string | number +>x : number | string +} + diff --git a/tests/baselines/reference/unionTypesAssignability.errors.txt b/tests/baselines/reference/unionTypesAssignability.errors.txt index cad61f41d1dbf..5be1ff3d246fa 100644 --- a/tests/baselines/reference/unionTypesAssignability.errors.txt +++ b/tests/baselines/reference/unionTypesAssignability.errors.txt @@ -24,9 +24,9 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTyp tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(43,1): error TS2322: Type 'string' is not assignable to type 'number'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(64,5): error TS2322: Type 'U' is not assignable to type 'T'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(65,5): error TS2322: Type 'T' is not assignable to type 'U'. -tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(69,5): error TS2322: Type 'T | U' is not assignable to type 'T'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(70,5): error TS2322: Type 'T | U' is not assignable to type 'T'. Type 'U' is not assignable to type 'T'. -tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(70,5): error TS2322: Type 'T | U' is not assignable to type 'U'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(71,5): error TS2322: Type 'T | U' is not assignable to type 'U'. Type 'T' is not assignable to type 'U'. @@ -142,6 +142,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTyp var x : T | U; x = t; // ok x = u; // ok + x = undefined; t = x; // error U not assignable to T ~ !!! error TS2322: Type 'T | U' is not assignable to type 'T'. diff --git a/tests/baselines/reference/unionTypesAssignability.js b/tests/baselines/reference/unionTypesAssignability.js index e07901347a58e..c710560800406 100644 --- a/tests/baselines/reference/unionTypesAssignability.js +++ b/tests/baselines/reference/unionTypesAssignability.js @@ -67,6 +67,7 @@ function foo(t: T, u: U) { var x : T | U; x = t; // ok x = u; // ok + x = undefined; t = x; // error U not assignable to T u = x; // error T not assignable to U } @@ -157,6 +158,7 @@ function foo(t, u) { var x; x = t; // ok x = u; // ok + x = undefined; t = x; // error U not assignable to T u = x; // error T not assignable to U } From 424074ba6bd1b9cf699cf6da15e95f5822272499 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 29 Mar 2016 19:58:17 -0700 Subject: [PATCH 15/67] Use type {} for vacuous type guards / New getTypeWithFacts function --- src/compiler/checker.ts | 224 ++++++++++++++++++++++++++++------------ 1 file changed, 157 insertions(+), 67 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d8c62dd2da3f9..871b485809b87 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -122,7 +122,7 @@ namespace ts { const resolvingFlowType = createIntrinsicType(TypeFlags.Void, "__resolving__"); const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const emptyUnionType = emptyObjectType; + const emptyUnionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); emptyGenericType.instantiations = {}; @@ -194,27 +194,74 @@ namespace ts { const diagnostics = createDiagnosticCollection(); - const primitiveTypeInfo: Map<{ type: Type; flags: TypeFlags }> = { - "string": { - type: stringType, - flags: TypeFlags.StringLike - }, - "number": { - type: numberType, - flags: TypeFlags.NumberLike - }, - "boolean": { - type: booleanType, - flags: TypeFlags.Boolean - }, - "symbol": { - type: esSymbolType, - flags: TypeFlags.ESSymbol - }, - "undefined": { - type: undefinedType, - flags: TypeFlags.ContainsUndefinedOrNull - } + const enum TypeFacts { + None = 0, + TypeofEQString = 1 << 0, // typeof x === "string" + TypeofEQNumber = 1 << 1, // typeof x === "number" + TypeofEQBoolean = 1 << 2, // typeof x === "boolean" + TypeofEQSymbol = 1 << 3, // typeof x === "symbol" + TypeofEQObject = 1 << 4, // typeof x === "object" + TypeofEQFunction = 1 << 5, // typeof x === "function" + TypeofNEString = 1 << 6, // typeof x !== "string" + TypeofNENumber = 1 << 7, // typeof x !== "number" + TypeofNEBoolean = 1 << 8, // typeof x !== "boolean" + TypeofNESymbol = 1 << 9, // typeof x !== "symbol" + TypeofNEObject = 1 << 10, // typeof x !== "object" + TypeofNEFunction = 1 << 11, // typeof x !== "function" + EQUndefined = 1 << 12, // x === undefined + EQNull = 1 << 13, // x === null + EQUndefinedOrNull = 1 << 14, // x == undefined / x == null + NEUndefined = 1 << 15, // x !== undefined + NENull = 1 << 16, // x !== null + NEUndefinedOrNull = 1 << 17, // x != undefined / x != null + Truthy = 1 << 18, // x + Falsy = 1 << 19, // !x + All = (1 << 20) - 1, + // The following members encode facts about particular kinds of types for use in the getTypeFacts function. + // The presence of a particular fact means that the given test is true for some (and possibly all) values + // of that kind of type. + StringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + StringFacts = StringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, + NumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + NumberFacts = NumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, + BooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + BooleanFacts = BooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, + SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + ObjectStrictFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + FunctionStrictFacts = TypeofEQFunction | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | EQUndefined | EQUndefinedOrNull | NENull | Falsy, + NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, + } + + const typeofEQFacts: Map = { + "string": TypeFacts.TypeofEQString, + "number": TypeFacts.TypeofEQNumber, + "boolean": TypeFacts.TypeofEQBoolean, + "symbol": TypeFacts.TypeofEQSymbol, + "undefined": TypeFacts.EQUndefined, + "object": TypeFacts.TypeofEQObject, + "function": TypeFacts.TypeofEQFunction + }; + + const typeofNEFacts: Map = { + "string": TypeFacts.TypeofNEString, + "number": TypeFacts.TypeofNENumber, + "boolean": TypeFacts.TypeofNEBoolean, + "symbol": TypeFacts.TypeofNESymbol, + "undefined": TypeFacts.NEUndefined, + "object": TypeFacts.TypeofNEObject, + "function": TypeFacts.TypeofNEFunction + }; + + const typeofTypesByName: Map = { + "string": stringType, + "number": numberType, + "boolean": booleanType, + "symbol": esSymbolType, + "undefined": undefinedType }; let jsxElementType: ObjectType; @@ -4823,7 +4870,7 @@ namespace ts { if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true; if (type.flags & TypeFlags.Null) typeSet.containsNull = true; } - else if (!contains(typeSet, type)) { + else if (type !== emptyUnionType && !contains(typeSet, type)) { typeSet.push(type); } } @@ -4879,7 +4926,9 @@ namespace ts { removeSubtypes(typeSet); } if (typeSet.length === 0) { - return typeSet.containsNull ? nullType : undefinedType; + return typeSet.containsNull ? nullType : + typeSet.containsUndefined ? undefinedType : + emptyUnionType; } else if (typeSet.length === 1) { return typeSet[0]; @@ -5637,7 +5686,7 @@ namespace ts { return isIdenticalTo(source, target); } - if (isTypeAny(target)) return Ternary.True; + if (target.flags & TypeFlags.Any) return Ternary.True; if (source.flags & TypeFlags.Undefined) { if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True; } @@ -6556,11 +6605,6 @@ namespace ts { return flags & TypeFlags.Nullable; } - function getNullableTypeOfKind(kind: TypeFlags) { - return kind & TypeFlags.Null ? kind & TypeFlags.Undefined ? - getUnionType([nullType, undefinedType]) : nullType : undefinedType; - } - function addNullableKind(type: Type, kind: TypeFlags): Type { if ((getNullableKind(type) & kind) !== kind) { const types = [type]; @@ -6576,7 +6620,10 @@ namespace ts { } function removeNullableKind(type: Type, kind: TypeFlags) { - if (type.flags & TypeFlags.Union && getNullableKind(type) & kind) { + if (!(getNullableKind(type) & kind)) { + return type; + } + if (type.flags & TypeFlags.Union) { let firstType: Type; let types: Type[]; for (const t of (type as UnionType).types) { @@ -6593,10 +6640,10 @@ namespace ts { } } if (firstType) { - type = types ? getUnionType(types) : firstType; + return types ? getUnionType(types) : firstType; } } - return type; + return emptyUnionType; } function getNonNullableType(type: Type): Type { @@ -7255,6 +7302,64 @@ namespace ts { return declaredType; } + function getTypeFacts(type: Type): TypeFacts { + const flags = type.flags; + if (flags & TypeFlags.StringLike) { + return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts; + } + if (flags & TypeFlags.NumberLike) { + return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; + } + if (flags & TypeFlags.Boolean) { + return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts; + } + if (flags & TypeFlags.ObjectType) { + const resolved = resolveStructuredTypeMembers(type); + return resolved.callSignatures.length || resolved.constructSignatures.length ? + strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : + strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; + } + if (flags & (TypeFlags.Void | TypeFlags.Undefined)) { + return TypeFacts.UndefinedFacts; + } + if (flags & TypeFlags.Null) { + return TypeFacts.NullFacts; + } + if (flags & TypeFlags.ESSymbol) { + return strictNullChecks ? TypeFacts.SymbolStrictFacts : TypeFacts.SymbolFacts; + } + if (flags & TypeFlags.TypeParameter) { + const constraint = getConstraintOfTypeParameter(type); + return constraint ? getTypeFacts(constraint) : TypeFacts.All; + } + if (flags & TypeFlags.Intersection) { + return reduceLeft((type).types, (flags, type) => flags |= getTypeFacts(type), TypeFacts.None); + } + return TypeFacts.All; + } + + function getTypeWithFacts(type: Type, include: TypeFacts) { + if (!(type.flags & TypeFlags.Union)) { + return getTypeFacts(type) & include ? type : emptyUnionType; + } + let firstType: Type; + let types: Type[]; + for (const t of (type as UnionType).types) { + if (getTypeFacts(t) & include) { + if (!firstType) { + firstType = t; + } + else { + if (!types) { + types = [firstType]; + } + types.push(t); + } + } + } + return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType; + } + function getNarrowedTypeOfReference(type: Type, reference: Node) { if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { return type; @@ -7407,7 +7512,7 @@ namespace ts { } function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { - return strictNullChecks && assumeTrue && isMatchingReference(expr, reference) ? getNonNullableType(type) : type; + return isMatchingReference(expr, reference) ? getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy) : type; } function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -7443,13 +7548,12 @@ namespace ts { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; - const exprNullableKind = doubleEquals ? TypeFlags.Nullable : - expr.right.kind === SyntaxKind.NullKeyword ? TypeFlags.Null : TypeFlags.Undefined; - if (assumeTrue) { - const nullableKind = getNullableKind(type) & exprNullableKind; - return nullableKind ? getNullableTypeOfKind(nullableKind) : type; - } - return removeNullableKind(type, exprNullableKind); + const facts = doubleEquals ? + assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : + expr.right.kind === SyntaxKind.NullKeyword ? + assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : + assumeTrue ? TypeFacts.EQUndefined: TypeFacts.NEUndefined; + return getTypeWithFacts(type, facts); } function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -7464,33 +7568,19 @@ namespace ts { expr.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - const typeInfo = primitiveTypeInfo[right.text]; - // Don't narrow `undefined` - if (typeInfo && typeInfo.type === undefinedType) { - return type; - } - let flags: TypeFlags; - if (typeInfo) { - flags = typeInfo.flags; - } - else { - assumeTrue = !assumeTrue; - flags = TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol | TypeFlags.Boolean; - } - // At this point we can bail if it's not a union - if (!(type.flags & TypeFlags.Union)) { - // If we're on the true branch and the type is a subtype, we should return the primitive type - if (assumeTrue && typeInfo && isTypeSubtypeOf(typeInfo.type, type)) { - return typeInfo.type; + if (assumeTrue && !(type.flags & TypeFlags.Union)) { + // We narrow a non-union type to an exact primitive type if the non-union type + // is a supertype of that primtive type. For example, type 'any' can be narrowed + // to one of the primitive types. + const targetType = getProperty(typeofTypesByName, right.text); + if (targetType && isTypeSubtypeOf(targetType, type)) { + return targetType; } - // If the active non-union type would be removed from a union by this type guard, return an empty union - return filterUnion(type) ? type : emptyUnionType; - } - return getUnionType(filter((type as UnionType).types, filterUnion), /*noSubtypeReduction*/ true); - - function filterUnion(type: Type) { - return assumeTrue === !!(type.flags & flags); } + const facts = assumeTrue ? + getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQObject : + getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEObject; + return getTypeWithFacts(type, facts); } function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -14243,7 +14333,7 @@ namespace ts { // Now that we've removed all the StringLike types, if no constituents remain, then the entire // arrayOrStringType was a string. - if (arrayType === emptyObjectType) { + if (arrayType === emptyUnionType) { return stringType; } } From c6f4de36071132081f05de5e32e79f9862418f83 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 29 Mar 2016 19:58:43 -0700 Subject: [PATCH 16/67] Remove unnecessary cast --- src/harness/loggedIO.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index 2c4d019d45b55..2763aeab34c82 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -149,7 +149,7 @@ namespace Playback { recordLog = createEmptyLog(); if (typeof underlying.args !== "function") { - recordLog.arguments = underlying.args; + recordLog.arguments = underlying.args; } }; From e53f390b3e0dfd6e3efd7f23f99a70e44a425a93 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 29 Mar 2016 19:59:05 -0700 Subject: [PATCH 17/67] Fix some tests --- .../typeGuards/typeGuardOfFormTypeOfBoolean.ts | 10 ++++------ .../typeGuards/typeGuardOfFormTypeOfNumber.ts | 10 ++++------ .../typeGuards/typeGuardOfFormTypeOfOther.ts | 10 ++++------ .../typeGuards/typeGuardOfFormTypeOfString.ts | 10 ++++------ 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts index fb699ce8ce0d4..37651071b7fcd 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfBoolean.ts @@ -41,12 +41,11 @@ else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum === "boolean") { - var z1: string | number = strOrNum; // string | number + let z1: {} = strOrNum; // {} } else { - var z2: string | number = strOrNum; // string | number + let z2: string | number = strOrNum; // string | number } @@ -78,10 +77,9 @@ else { bool = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum !== "boolean") { - var z1: string | number = strOrNum; // string | number + let z1: string | number = strOrNum; // string | number } else { - var z2: string | number = strOrNum; // string | number + let z2: {} = strOrNum; // {} } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts index 2868b114077af..b4cdf81660ea0 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfNumber.ts @@ -41,12 +41,11 @@ else { c = numOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool === "number") { - var y1: string | boolean = strOrBool; // string | boolean + let y1: {} = strOrBool; // {} } else { - var y2: string | boolean = strOrBool; // string | boolean + let y2: string | boolean = strOrBool; // string | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -77,10 +76,9 @@ else { num = numOrC; // number } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool !== "number") { - var y1: string | boolean = strOrBool; // string | boolean + let y1: string | boolean = strOrBool; // string | boolean } else { - var y2: string | boolean = strOrBool; // string | boolean + let y2: {} = strOrBool; // {} } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts index ac3403d8151ad..44b064c321941 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfOther.ts @@ -37,12 +37,11 @@ else { var r4: boolean = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q1: {} = strOrNumOrBool; // {} } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q2: string | number | boolean = strOrNumOrBool; // string | number | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -67,10 +66,9 @@ else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool !== "Object") { - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q1: string | number | boolean = strOrNumOrBool; // string | number | boolean } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q2: {} = strOrNumOrBool; // {} } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts index a356858e6c8ea..f742124708f80 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfString.ts @@ -41,12 +41,11 @@ else { c = strOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool === "string") { - var x1: number | boolean = numOrBool; // number | boolean + let x1: {} = numOrBool; // {} } else { - var x2: number | boolean = numOrBool; // number | boolean + let x2: number | boolean = numOrBool; // number | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -77,10 +76,9 @@ else { str = strOrC; // string } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool !== "string") { - var x1: number | boolean = numOrBool; // number | boolean + let x1: number | boolean = numOrBool; // number | boolean } else { - var x2: number | boolean = numOrBool; // number | boolean + let x2: {} = numOrBool; // {} } From a38d86391002b7a9bb0ba4c666be3bae015f1cce Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 29 Mar 2016 20:00:40 -0700 Subject: [PATCH 18/67] Accepting new baselines --- .../reference/typeGuardOfFormTypeOfBoolean.js | 16 ++++----- .../typeGuardOfFormTypeOfBoolean.symbols | 18 +++++----- .../typeGuardOfFormTypeOfBoolean.types | 24 ++++++------- .../reference/typeGuardOfFormTypeOfNumber.js | 16 ++++----- .../typeGuardOfFormTypeOfNumber.symbols | 22 ++++++------ .../typeGuardOfFormTypeOfNumber.types | 22 ++++++------ .../reference/typeGuardOfFormTypeOfOther.js | 16 ++++----- .../typeGuardOfFormTypeOfOther.symbols | 30 ++++++++-------- .../typeGuardOfFormTypeOfOther.types | 24 ++++++------- .../reference/typeGuardOfFormTypeOfString.js | 16 ++++----- .../typeGuardOfFormTypeOfString.symbols | 18 +++++----- .../typeGuardOfFormTypeOfString.types | 18 +++++----- .../typeGuardTautologicalConsistiency.types | 6 ++-- .../reference/typeGuardTypeOfUndefined.types | 34 +++++++++---------- 14 files changed, 124 insertions(+), 156 deletions(-) diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js index e9e11275d0dd5..7d68b89337300 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.js @@ -42,12 +42,11 @@ else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum === "boolean") { - var z1: string | number = strOrNum; // string | number + let z1: {} = strOrNum; // {} } else { - var z2: string | number = strOrNum; // string | number + let z2: string | number = strOrNum; // string | number } @@ -79,12 +78,11 @@ else { bool = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum !== "boolean") { - var z1: string | number = strOrNum; // string | number + let z1: string | number = strOrNum; // string | number } else { - var z2: string | number = strOrNum; // string | number + let z2: {} = strOrNum; // {} } @@ -134,9 +132,8 @@ if (typeof boolOrC === "boolean") { else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum === "boolean") { - var z1 = strOrNum; // string | number + var z1 = strOrNum; // {} } else { var z2 = strOrNum; // string | number @@ -168,10 +165,9 @@ if (typeof boolOrC !== "boolean") { else { bool = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum !== "boolean") { var z1 = strOrNum; // string | number } else { - var z2 = strOrNum; // string | number + var z2 = strOrNum; // {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.symbols b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.symbols index d387951922817..dbef84c28c7e3 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.symbols +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.symbols @@ -93,17 +93,16 @@ else { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfBoolean.ts, 11, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum === "boolean") { >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) - var z1: string | number = strOrNum; // string | number ->z1 : Symbol(z1, Decl(typeGuardOfFormTypeOfBoolean.ts, 45, 7), Decl(typeGuardOfFormTypeOfBoolean.ts, 82, 7)) + let z1: {} = strOrNum; // {} +>z1 : Symbol(z1, Decl(typeGuardOfFormTypeOfBoolean.ts, 44, 7)) >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) } else { - var z2: string | number = strOrNum; // string | number ->z2 : Symbol(z2, Decl(typeGuardOfFormTypeOfBoolean.ts, 48, 7), Decl(typeGuardOfFormTypeOfBoolean.ts, 85, 7)) + let z2: string | number = strOrNum; // string | number +>z2 : Symbol(z2, Decl(typeGuardOfFormTypeOfBoolean.ts, 47, 7)) >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) } @@ -160,17 +159,16 @@ else { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfBoolean.ts, 11, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum !== "boolean") { >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) - var z1: string | number = strOrNum; // string | number ->z1 : Symbol(z1, Decl(typeGuardOfFormTypeOfBoolean.ts, 45, 7), Decl(typeGuardOfFormTypeOfBoolean.ts, 82, 7)) + let z1: string | number = strOrNum; // string | number +>z1 : Symbol(z1, Decl(typeGuardOfFormTypeOfBoolean.ts, 80, 7)) >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) } else { - var z2: string | number = strOrNum; // string | number ->z2 : Symbol(z2, Decl(typeGuardOfFormTypeOfBoolean.ts, 48, 7), Decl(typeGuardOfFormTypeOfBoolean.ts, 85, 7)) + let z2: {} = strOrNum; // {} +>z2 : Symbol(z2, Decl(typeGuardOfFormTypeOfBoolean.ts, 83, 7)) >strOrNum : Symbol(strOrNum, Decl(typeGuardOfFormTypeOfBoolean.ts, 5, 3)) } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types index 9d9f28548be75..a5dcc9207cd71 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types @@ -113,19 +113,18 @@ else { >boolOrC : C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum === "boolean") { >typeof strOrNum === "boolean" : boolean >typeof strOrNum : string >strOrNum : string | number >"boolean" : string - var z1: string | number = strOrNum; // string | number ->z1 : string | number ->strOrNum : string | number + let z1: {} = strOrNum; // {} +>z1 : {} +>strOrNum : {} } else { - var z2: string | number = strOrNum; // string | number + let z2: string | number = strOrNum; // string | number >z2 : string | number >strOrNum : string | number } @@ -137,7 +136,7 @@ else { if (typeof strOrBool !== "boolean") { >typeof strOrBool !== "boolean" : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >"boolean" : string str = strOrBool; // string @@ -154,7 +153,7 @@ else { if (typeof numOrBool !== "boolean") { >typeof numOrBool !== "boolean" : boolean >typeof numOrBool : string ->numOrBool : number | boolean +>numOrBool : boolean | number >"boolean" : string num = numOrBool; // number @@ -171,7 +170,7 @@ else { if (typeof strOrNumOrBool !== "boolean") { >typeof strOrNumOrBool !== "boolean" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : boolean | string | number >"boolean" : string strOrNum = strOrNumOrBool; // string | number @@ -203,20 +202,19 @@ else { >boolOrC : boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNum !== "boolean") { >typeof strOrNum !== "boolean" : boolean >typeof strOrNum : string >strOrNum : string | number >"boolean" : string - var z1: string | number = strOrNum; // string | number + let z1: string | number = strOrNum; // string | number >z1 : string | number >strOrNum : string | number } else { - var z2: string | number = strOrNum; // string | number ->z2 : string | number ->strOrNum : string | number + let z2: {} = strOrNum; // {} +>z2 : {} +>strOrNum : {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js index 66fcff0c3870e..3bea6e87d2b29 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.js @@ -42,12 +42,11 @@ else { c = numOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool === "number") { - var y1: string | boolean = strOrBool; // string | boolean + let y1: {} = strOrBool; // {} } else { - var y2: string | boolean = strOrBool; // string | boolean + let y2: string | boolean = strOrBool; // string | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -78,12 +77,11 @@ else { num = numOrC; // number } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool !== "number") { - var y1: string | boolean = strOrBool; // string | boolean + let y1: string | boolean = strOrBool; // string | boolean } else { - var y2: string | boolean = strOrBool; // string | boolean + let y2: {} = strOrBool; // {} } @@ -133,9 +131,8 @@ if (typeof numOrC === "number") { else { c = numOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool === "number") { - var y1 = strOrBool; // string | boolean + var y1 = strOrBool; // {} } else { var y2 = strOrBool; // string | boolean @@ -167,10 +164,9 @@ if (typeof numOrC !== "number") { else { num = numOrC; // number } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool !== "number") { var y1 = strOrBool; // string | boolean } else { - var y2 = strOrBool; // string | boolean + var y2 = strOrBool; // {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.symbols b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.symbols index 2fd48389ae862..f38c348131ee8 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.symbols +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.symbols @@ -65,7 +65,7 @@ if (typeof numOrBool === "number") { } else { var x: number | boolean = numOrBool; // number | boolean ->x : Symbol(x, Decl(typeGuardOfFormTypeOfNumber.ts, 28, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 61, 7)) +>x : Symbol(x, Decl(typeGuardOfFormTypeOfNumber.ts, 28, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 60, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 7, 3)) } if (typeof strOrNumOrBool === "number") { @@ -93,17 +93,16 @@ else { >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfNumber.ts, 10, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool === "number") { >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) - var y1: string | boolean = strOrBool; // string | boolean ->y1 : Symbol(y1, Decl(typeGuardOfFormTypeOfNumber.ts, 45, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 81, 7)) + let y1: {} = strOrBool; // {} +>y1 : Symbol(y1, Decl(typeGuardOfFormTypeOfNumber.ts, 44, 7)) >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) } else { - var y2: string | boolean = strOrBool; // string | boolean ->y2 : Symbol(y2, Decl(typeGuardOfFormTypeOfNumber.ts, 48, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 84, 7)) + let y2: string | boolean = strOrBool; // string | boolean +>y2 : Symbol(y2, Decl(typeGuardOfFormTypeOfNumber.ts, 47, 7)) >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) } @@ -126,7 +125,7 @@ if (typeof numOrBool !== "number") { >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 7, 3)) var x: number | boolean = numOrBool; // number | boolean ->x : Symbol(x, Decl(typeGuardOfFormTypeOfNumber.ts, 28, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 61, 7)) +>x : Symbol(x, Decl(typeGuardOfFormTypeOfNumber.ts, 28, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 60, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 7, 3)) } else { @@ -159,17 +158,16 @@ else { >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfNumber.ts, 10, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool !== "number") { >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) - var y1: string | boolean = strOrBool; // string | boolean ->y1 : Symbol(y1, Decl(typeGuardOfFormTypeOfNumber.ts, 45, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 81, 7)) + let y1: string | boolean = strOrBool; // string | boolean +>y1 : Symbol(y1, Decl(typeGuardOfFormTypeOfNumber.ts, 79, 7)) >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) } else { - var y2: string | boolean = strOrBool; // string | boolean ->y2 : Symbol(y2, Decl(typeGuardOfFormTypeOfNumber.ts, 48, 7), Decl(typeGuardOfFormTypeOfNumber.ts, 84, 7)) + let y2: {} = strOrBool; // {} +>y2 : Symbol(y2, Decl(typeGuardOfFormTypeOfNumber.ts, 82, 7)) >strOrBool : Symbol(strOrBool, Decl(typeGuardOfFormTypeOfNumber.ts, 6, 3)) } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types index d3caef24efded..99f927b713731 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types @@ -112,19 +112,18 @@ else { >numOrC : C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool === "number") { >typeof strOrBool === "number" : boolean >typeof strOrBool : string >strOrBool : string | boolean >"number" : string - var y1: string | boolean = strOrBool; // string | boolean ->y1 : string | boolean ->strOrBool : string | boolean + let y1: {} = strOrBool; // {} +>y1 : {} +>strOrBool : {} } else { - var y2: string | boolean = strOrBool; // string | boolean + let y2: string | boolean = strOrBool; // string | boolean >y2 : string | boolean >strOrBool : string | boolean } @@ -135,7 +134,7 @@ else { if (typeof strOrNum !== "number") { >typeof strOrNum !== "number" : boolean >typeof strOrNum : string ->strOrNum : string | number +>strOrNum : number | string >"number" : string str === strOrNum; // string @@ -168,7 +167,7 @@ else { if (typeof strOrNumOrBool !== "number") { >typeof strOrNumOrBool !== "number" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : number | string | boolean >"number" : string strOrBool = strOrNumOrBool; // string | boolean @@ -200,20 +199,19 @@ else { >numOrC : number } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrBool !== "number") { >typeof strOrBool !== "number" : boolean >typeof strOrBool : string >strOrBool : string | boolean >"number" : string - var y1: string | boolean = strOrBool; // string | boolean + let y1: string | boolean = strOrBool; // string | boolean >y1 : string | boolean >strOrBool : string | boolean } else { - var y2: string | boolean = strOrBool; // string | boolean ->y2 : string | boolean ->strOrBool : string | boolean + let y2: {} = strOrBool; // {} +>y2 : {} +>strOrBool : {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.js b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js index b1e6ca2656627..ee0b6f6633279 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.js +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.js @@ -38,12 +38,11 @@ else { var r4: boolean = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q1: {} = strOrNumOrBool; // {} } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q2: string | number | boolean = strOrNumOrBool; // string | number | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -68,12 +67,11 @@ else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool !== "Object") { - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q1: string | number | boolean = strOrNumOrBool; // string | number | boolean } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q2: {} = strOrNumOrBool; // {} } @@ -118,9 +116,8 @@ if (typeof boolOrC === "Object") { else { var r4 = boolOrC; // boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { - var q1 = strOrNumOrBool; // string | number | boolean + var q1 = strOrNumOrBool; // {} } else { var q2 = strOrNumOrBool; // string | number | boolean @@ -146,10 +143,9 @@ if (typeof boolOrC !== "Object") { else { c = boolOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool !== "Object") { var q1 = strOrNumOrBool; // string | number | boolean } else { - var q2 = strOrNumOrBool; // string | number | boolean + var q2 = strOrNumOrBool; // {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols b/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols index ba759871a9b35..a8760feda4515 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.symbols @@ -57,7 +57,7 @@ if (typeof strOrC === "Object") { } else { var r2: string = strOrC; // string ->r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 51, 7)) +>r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 50, 7)) >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) } if (typeof numOrC === "Object") { @@ -69,7 +69,7 @@ if (typeof numOrC === "Object") { } else { var r3: number = numOrC; // number ->r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 57, 7)) +>r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 56, 7)) >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) } if (typeof boolOrC === "Object") { @@ -81,21 +81,20 @@ if (typeof boolOrC === "Object") { } else { var r4: boolean = boolOrC; // boolean ->r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 63, 7)) +>r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 62, 7)) >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q1 : Symbol(q1, Decl(typeGuardOfFormTypeOfOther.ts, 41, 7), Decl(typeGuardOfFormTypeOfOther.ts, 71, 7)) + let q1: {} = strOrNumOrBool; // {} +>q1 : Symbol(q1, Decl(typeGuardOfFormTypeOfOther.ts, 40, 7)) >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q2 : Symbol(q2, Decl(typeGuardOfFormTypeOfOther.ts, 44, 7), Decl(typeGuardOfFormTypeOfOther.ts, 74, 7)) + let q2: string | number | boolean = strOrNumOrBool; // string | number | boolean +>q2 : Symbol(q2, Decl(typeGuardOfFormTypeOfOther.ts, 43, 7)) >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) } @@ -106,7 +105,7 @@ if (typeof strOrC !== "Object") { >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) var r2: string = strOrC; // string ->r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 51, 7)) +>r2 : Symbol(r2, Decl(typeGuardOfFormTypeOfOther.ts, 24, 7), Decl(typeGuardOfFormTypeOfOther.ts, 50, 7)) >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfOther.ts, 9, 3)) } else { @@ -118,7 +117,7 @@ if (typeof numOrC !== "Object") { >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) var r3: number = numOrC; // number ->r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 57, 7)) +>r3 : Symbol(r3, Decl(typeGuardOfFormTypeOfOther.ts, 30, 7), Decl(typeGuardOfFormTypeOfOther.ts, 56, 7)) >numOrC : Symbol(numOrC, Decl(typeGuardOfFormTypeOfOther.ts, 10, 3)) } else { @@ -130,7 +129,7 @@ if (typeof boolOrC !== "Object") { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) var r4: boolean = boolOrC; // boolean ->r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 63, 7)) +>r4 : Symbol(r4, Decl(typeGuardOfFormTypeOfOther.ts, 36, 7), Decl(typeGuardOfFormTypeOfOther.ts, 62, 7)) >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } else { @@ -139,17 +138,16 @@ else { >boolOrC : Symbol(boolOrC, Decl(typeGuardOfFormTypeOfOther.ts, 11, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool !== "Object") { >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q1 : Symbol(q1, Decl(typeGuardOfFormTypeOfOther.ts, 41, 7), Decl(typeGuardOfFormTypeOfOther.ts, 71, 7)) + let q1: string | number | boolean = strOrNumOrBool; // string | number | boolean +>q1 : Symbol(q1, Decl(typeGuardOfFormTypeOfOther.ts, 69, 7)) >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q2 : Symbol(q2, Decl(typeGuardOfFormTypeOfOther.ts, 44, 7), Decl(typeGuardOfFormTypeOfOther.ts, 74, 7)) + let q2: {} = strOrNumOrBool; // {} +>q2 : Symbol(q2, Decl(typeGuardOfFormTypeOfOther.ts, 72, 7)) >strOrNumOrBool : Symbol(strOrNumOrBool, Decl(typeGuardOfFormTypeOfOther.ts, 8, 3)) } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types index 5cec356719493..aba8e429b8ccc 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfOther.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfOther.types @@ -97,19 +97,18 @@ else { >boolOrC : boolean } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool === "Object") { >typeof strOrNumOrBool === "Object" : boolean >typeof strOrNumOrBool : string >strOrNumOrBool : string | number | boolean >"Object" : string - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q1 : string | number | boolean ->strOrNumOrBool : string | number | boolean + let q1: {} = strOrNumOrBool; // {} +>q1 : {} +>strOrNumOrBool : {} } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q2: string | number | boolean = strOrNumOrBool; // string | number | boolean >q2 : string | number | boolean >strOrNumOrBool : string | number | boolean } @@ -120,7 +119,7 @@ else { if (typeof strOrC !== "Object") { >typeof strOrC !== "Object" : boolean >typeof strOrC : string ->strOrC : string | C +>strOrC : C | string >"Object" : string var r2: string = strOrC; // string @@ -136,7 +135,7 @@ else { if (typeof numOrC !== "Object") { >typeof numOrC !== "Object" : boolean >typeof numOrC : string ->numOrC : number | C +>numOrC : C | number >"Object" : string var r3: number = numOrC; // number @@ -152,7 +151,7 @@ else { if (typeof boolOrC !== "Object") { >typeof boolOrC !== "Object" : boolean >typeof boolOrC : string ->boolOrC : boolean | C +>boolOrC : C | boolean >"Object" : string var r4: boolean = boolOrC; // boolean @@ -166,20 +165,19 @@ else { >boolOrC : C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof strOrNumOrBool !== "Object") { >typeof strOrNumOrBool !== "Object" : boolean >typeof strOrNumOrBool : string >strOrNumOrBool : string | number | boolean >"Object" : string - var q1: string | number | boolean = strOrNumOrBool; // string | number | boolean + let q1: string | number | boolean = strOrNumOrBool; // string | number | boolean >q1 : string | number | boolean >strOrNumOrBool : string | number | boolean } else { - var q2: string | number | boolean = strOrNumOrBool; // string | number | boolean ->q2 : string | number | boolean ->strOrNumOrBool : string | number | boolean + let q2: {} = strOrNumOrBool; // {} +>q2 : {} +>strOrNumOrBool : {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.js b/tests/baselines/reference/typeGuardOfFormTypeOfString.js index 5626396f73da9..d79f73a87c5f1 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfString.js +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.js @@ -42,12 +42,11 @@ else { c = strOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool === "string") { - var x1: number | boolean = numOrBool; // number | boolean + let x1: {} = numOrBool; // {} } else { - var x2: number | boolean = numOrBool; // number | boolean + let x2: number | boolean = numOrBool; // number | boolean } // A type guard of the form typeof x !== s, where s is a string literal, @@ -78,12 +77,11 @@ else { str = strOrC; // string } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool !== "string") { - var x1: number | boolean = numOrBool; // number | boolean + let x1: number | boolean = numOrBool; // number | boolean } else { - var x2: number | boolean = numOrBool; // number | boolean + let x2: {} = numOrBool; // {} } @@ -133,9 +131,8 @@ if (typeof strOrC === "string") { else { c = strOrC; // C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool === "string") { - var x1 = numOrBool; // number | boolean + var x1 = numOrBool; // {} } else { var x2 = numOrBool; // number | boolean @@ -167,10 +164,9 @@ if (typeof strOrC !== "string") { else { str = strOrC; // string } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool !== "string") { var x1 = numOrBool; // number | boolean } else { - var x2 = numOrBool; // number | boolean + var x2 = numOrBool; // {} } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.symbols b/tests/baselines/reference/typeGuardOfFormTypeOfString.symbols index d3209189f8328..4a77ecb1ffb11 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfString.symbols +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.symbols @@ -93,17 +93,16 @@ else { >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfString.ts, 9, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool === "string") { >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) - var x1: number | boolean = numOrBool; // number | boolean ->x1 : Symbol(x1, Decl(typeGuardOfFormTypeOfString.ts, 45, 7), Decl(typeGuardOfFormTypeOfString.ts, 81, 7)) + let x1: {} = numOrBool; // {} +>x1 : Symbol(x1, Decl(typeGuardOfFormTypeOfString.ts, 44, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) } else { - var x2: number | boolean = numOrBool; // number | boolean ->x2 : Symbol(x2, Decl(typeGuardOfFormTypeOfString.ts, 48, 7), Decl(typeGuardOfFormTypeOfString.ts, 84, 7)) + let x2: number | boolean = numOrBool; // number | boolean +>x2 : Symbol(x2, Decl(typeGuardOfFormTypeOfString.ts, 47, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) } @@ -159,17 +158,16 @@ else { >strOrC : Symbol(strOrC, Decl(typeGuardOfFormTypeOfString.ts, 9, 3)) } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool !== "string") { >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) - var x1: number | boolean = numOrBool; // number | boolean ->x1 : Symbol(x1, Decl(typeGuardOfFormTypeOfString.ts, 45, 7), Decl(typeGuardOfFormTypeOfString.ts, 81, 7)) + let x1: number | boolean = numOrBool; // number | boolean +>x1 : Symbol(x1, Decl(typeGuardOfFormTypeOfString.ts, 79, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) } else { - var x2: number | boolean = numOrBool; // number | boolean ->x2 : Symbol(x2, Decl(typeGuardOfFormTypeOfString.ts, 48, 7), Decl(typeGuardOfFormTypeOfString.ts, 84, 7)) + let x2: {} = numOrBool; // {} +>x2 : Symbol(x2, Decl(typeGuardOfFormTypeOfString.ts, 82, 7)) >numOrBool : Symbol(numOrBool, Decl(typeGuardOfFormTypeOfString.ts, 7, 3)) } diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.types b/tests/baselines/reference/typeGuardOfFormTypeOfString.types index d6a382261ac55..c77f9f3207ac1 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfString.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.types @@ -113,19 +113,18 @@ else { >strOrC : C } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool === "string") { >typeof numOrBool === "string" : boolean >typeof numOrBool : string >numOrBool : number | boolean >"string" : string - var x1: number | boolean = numOrBool; // number | boolean ->x1 : number | boolean ->numOrBool : number | boolean + let x1: {} = numOrBool; // {} +>x1 : {} +>numOrBool : {} } else { - var x2: number | boolean = numOrBool; // number | boolean + let x2: number | boolean = numOrBool; // number | boolean >x2 : number | boolean >numOrBool : number | boolean } @@ -202,20 +201,19 @@ else { >strOrC : string } -// Narrowing occurs only if target type is a subtype of variable type if (typeof numOrBool !== "string") { >typeof numOrBool !== "string" : boolean >typeof numOrBool : string >numOrBool : number | boolean >"string" : string - var x1: number | boolean = numOrBool; // number | boolean + let x1: number | boolean = numOrBool; // number | boolean >x1 : number | boolean >numOrBool : number | boolean } else { - var x2: number | boolean = numOrBool; // number | boolean ->x2 : number | boolean ->numOrBool : number | boolean + let x2: {} = numOrBool; // {} +>x2 : {} +>numOrBool : {} } diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types index d758dcde22bc8..100e528f61693 100644 --- a/tests/baselines/reference/typeGuardTautologicalConsistiency.types +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -15,7 +15,7 @@ if (typeof stringOrNumber === "number") { >"number" : string stringOrNumber; ->stringOrNumber : string | number +>stringOrNumber : {} } } @@ -23,7 +23,7 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >typeof stringOrNumber === "number" && typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber === "number" : boolean >typeof stringOrNumber : string ->stringOrNumber : string | number +>stringOrNumber : number | string >"number" : string >typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber : string @@ -31,6 +31,6 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >"number" : string stringOrNumber; ->stringOrNumber : string | number +>stringOrNumber : {} } diff --git a/tests/baselines/reference/typeGuardTypeOfUndefined.types b/tests/baselines/reference/typeGuardTypeOfUndefined.types index 6cf57e1a1ddce..7073d9aeacbfb 100644 --- a/tests/baselines/reference/typeGuardTypeOfUndefined.types +++ b/tests/baselines/reference/typeGuardTypeOfUndefined.types @@ -26,7 +26,7 @@ function test1(a: any) { } else { a; ->a : any +>a : undefined } } @@ -43,15 +43,15 @@ function test2(a: any) { if (typeof a === "boolean") { >typeof a === "boolean" : boolean >typeof a : string ->a : any +>a : undefined >"boolean" : string a; ->a : boolean +>a : {} } else { a; ->a : any +>a : undefined } } else { @@ -76,7 +76,7 @@ function test3(a: any) { >"boolean" : string a; ->a : any +>a : boolean } else { a; @@ -121,7 +121,7 @@ function test5(a: boolean | void) { if (typeof a === "boolean") { >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | void +>a : boolean >"boolean" : string a; @@ -129,7 +129,7 @@ function test5(a: boolean | void) { } else { a; ->a : void +>a : {} } } else { @@ -164,7 +164,7 @@ function test6(a: boolean | void) { } else { a; ->a : boolean | void +>a : boolean } } @@ -180,7 +180,7 @@ function test7(a: boolean | void) { >"undefined" : string >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | void +>a : boolean >"boolean" : string a; @@ -188,7 +188,7 @@ function test7(a: boolean | void) { } else { a; ->a : void +>a : {} } } @@ -204,7 +204,7 @@ function test8(a: boolean | void) { >"undefined" : string >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | void +>a : boolean >"boolean" : string a; @@ -337,7 +337,7 @@ function test13(a: boolean | number | void) { if (typeof a === "boolean") { >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | number | void +>a : boolean | number >"boolean" : string a; @@ -345,7 +345,7 @@ function test13(a: boolean | number | void) { } else { a; ->a : number | void +>a : number } } else { @@ -380,7 +380,7 @@ function test14(a: boolean | number | void) { } else { a; ->a : boolean | number | void +>a : boolean | number } } @@ -396,7 +396,7 @@ function test15(a: boolean | number | void) { >"undefined" : string >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | number | void +>a : boolean | number >"boolean" : string a; @@ -404,7 +404,7 @@ function test15(a: boolean | number | void) { } else { a; ->a : number | void +>a : number } } @@ -420,7 +420,7 @@ function test16(a: boolean | number | void) { >"undefined" : string >typeof a === "boolean" : boolean >typeof a : string ->a : boolean | number | void +>a : boolean | number >"boolean" : string a; From 3d0fa31a9d3f10896707915966ab7cd2aed33787 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 29 Mar 2016 20:17:20 -0700 Subject: [PATCH 19/67] Delete removeNullableKind, use getTypeWithFacts instead --- src/compiler/checker.ts | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 871b485809b87..1e05702d9788f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2757,7 +2757,7 @@ namespace ts { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { - type = removeNullableKind(type, TypeFlags.Undefined); + type = getTypeWithFacts(type, TypeFacts.NEUndefined); } return type; } @@ -6619,35 +6619,8 @@ namespace ts { return type; } - function removeNullableKind(type: Type, kind: TypeFlags) { - if (!(getNullableKind(type) & kind)) { - return type; - } - if (type.flags & TypeFlags.Union) { - let firstType: Type; - let types: Type[]; - for (const t of (type as UnionType).types) { - if (!(t.flags & kind)) { - if (!firstType) { - firstType = t; - } - else { - if (!types) { - types = [firstType]; - } - types.push(t); - } - } - } - if (firstType) { - return types ? getUnionType(types) : firstType; - } - } - return emptyUnionType; - } - function getNonNullableType(type: Type): Type { - return strictNullChecks ? removeNullableKind(type, TypeFlags.Nullable) : type; + return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } /** From ce81ba51566a328f6ee7c9f41f570675bea7ca3f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 31 Mar 2016 10:07:28 -0700 Subject: [PATCH 20/67] Support unknown types (host object names) in typeof type guards --- src/compiler/checker.ts | 64 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e05702d9788f..da6548f8c699d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -196,44 +196,46 @@ namespace ts { const enum TypeFacts { None = 0, - TypeofEQString = 1 << 0, // typeof x === "string" - TypeofEQNumber = 1 << 1, // typeof x === "number" - TypeofEQBoolean = 1 << 2, // typeof x === "boolean" - TypeofEQSymbol = 1 << 3, // typeof x === "symbol" - TypeofEQObject = 1 << 4, // typeof x === "object" - TypeofEQFunction = 1 << 5, // typeof x === "function" - TypeofNEString = 1 << 6, // typeof x !== "string" - TypeofNENumber = 1 << 7, // typeof x !== "number" - TypeofNEBoolean = 1 << 8, // typeof x !== "boolean" - TypeofNESymbol = 1 << 9, // typeof x !== "symbol" - TypeofNEObject = 1 << 10, // typeof x !== "object" - TypeofNEFunction = 1 << 11, // typeof x !== "function" - EQUndefined = 1 << 12, // x === undefined - EQNull = 1 << 13, // x === null - EQUndefinedOrNull = 1 << 14, // x == undefined / x == null - NEUndefined = 1 << 15, // x !== undefined - NENull = 1 << 16, // x !== null - NEUndefinedOrNull = 1 << 17, // x != undefined / x != null - Truthy = 1 << 18, // x - Falsy = 1 << 19, // !x - All = (1 << 20) - 1, + TypeofEQString = 1 << 0, // typeof x === "string" + TypeofEQNumber = 1 << 1, // typeof x === "number" + TypeofEQBoolean = 1 << 2, // typeof x === "boolean" + TypeofEQSymbol = 1 << 3, // typeof x === "symbol" + TypeofEQObject = 1 << 4, // typeof x === "object" + TypeofEQFunction = 1 << 5, // typeof x === "function" + TypeofEQHostObject = 1 << 6, // typeof x === "xxx" + TypeofNEString = 1 << 7, // typeof x !== "string" + TypeofNENumber = 1 << 8, // typeof x !== "number" + TypeofNEBoolean = 1 << 9, // typeof x !== "boolean" + TypeofNESymbol = 1 << 10, // typeof x !== "symbol" + TypeofNEObject = 1 << 11, // typeof x !== "object" + TypeofNEFunction = 1 << 12, // typeof x !== "function" + TypeofNEHostObject = 1 << 13, // typeof x !== "xxx" + EQUndefined = 1 << 14, // x === undefined + EQNull = 1 << 15, // x === null + EQUndefinedOrNull = 1 << 16, // x == undefined / x == null + NEUndefined = 1 << 17, // x !== undefined + NENull = 1 << 18, // x !== null + NEUndefinedOrNull = 1 << 19, // x != undefined / x != null + Truthy = 1 << 20, // x + Falsy = 1 << 21, // !x + All = (1 << 22) - 1, // The following members encode facts about particular kinds of types for use in the getTypeFacts function. // The presence of a particular fact means that the given test is true for some (and possibly all) values // of that kind of type. - StringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + StringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, StringFacts = StringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, - NumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + NumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, NumberFacts = NumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, - BooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, + BooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy, BooleanFacts = BooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull, - SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | EQUndefined | EQUndefinedOrNull | NENull | Falsy, - NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, + UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, + NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } const typeofEQFacts: Map = { @@ -7551,8 +7553,8 @@ namespace ts { } } const facts = assumeTrue ? - getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQObject : - getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEObject; + getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQHostObject : + getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } From 354fd10a2e12e49407615648579c8c00ee34e94a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 1 Apr 2016 09:53:44 -0700 Subject: [PATCH 21/67] Separate error messages for 'null', 'undefined', or both. --- src/compiler/checker.ts | 10 ++++++++-- src/compiler/diagnosticMessages.json | 10 +++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index da6548f8c699d..6fc60c5c78a5a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9554,8 +9554,14 @@ namespace ts { function checkNonNullExpression(node: Expression | QualifiedName) { const type = checkExpression(node); - if (strictNullChecks && getNullableKind(type)) { - error(node, Diagnostics.Object_is_possibly_null_or_undefined); + if (strictNullChecks) { + const kind = getNullableKind(type); + if (kind) { + error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ? + Diagnostics.Object_is_possibly_null_or_undefined : + Diagnostics.Object_is_possibly_undefined : + Diagnostics.Object_is_possibly_null); + } return getNonNullableType(type); } return type; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d7c2fac27102f..ebd3202d3a7d5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1727,10 +1727,18 @@ "category": "Error", "code": 2530 }, - "Object is possibly 'null' or 'undefined'.": { + "Object is possibly 'null'.": { "category": "Error", "code": 2531 }, + "Object is possibly 'undefined'.": { + "category": "Error", + "code": 2532 + }, + "Object is possibly 'null' or 'undefined'.": { + "category": "Error", + "code": 2533 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 From 5179dd6ada16a9b0eaf1e84eb9453df8e4772aaa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 8 Apr 2016 09:13:47 -0700 Subject: [PATCH 22/67] Flow analysis of &&, ||, and destructuring assignments --- src/compiler/binder.ts | 279 +++++++++++++++++++++++++++----------- src/compiler/checker.ts | 179 ++++++++++-------------- src/compiler/types.ts | 6 +- src/compiler/utilities.ts | 25 ++++ 4 files changed, 302 insertions(+), 187 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index cfeb76827bfc0..39dca4070a7b3 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -105,8 +105,10 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; let currentFlow: FlowNode; - let breakTarget: FlowLabel; - let continueTarget: FlowLabel; + let currentBreakTarget: FlowLabel; + let currentContinueTarget: FlowLabel; + let currentTrueTarget: FlowLabel; + let currentFalseTarget: FlowLabel; let preSwitchCaseFlow: FlowNode; let activeLabels: ActiveLabel[]; @@ -151,8 +153,10 @@ namespace ts { seenThisKeyword = false; hasExplicitReturn = false; currentFlow = undefined; - breakTarget = undefined; - continueTarget = undefined; + currentBreakTarget = undefined; + currentContinueTarget = undefined; + currentTrueTarget = undefined; + currentFalseTarget = undefined; activeLabels = undefined; hasClassExtends = false; hasAsyncFunctions = false; @@ -437,6 +441,8 @@ namespace ts { let savedCurrentFlow: FlowNode; let savedBreakTarget: FlowLabel; let savedContinueTarget: FlowLabel; + let savedTrueTarget: FlowLabel; + let savedFalseTarget: FlowLabel; let savedActiveLabels: ActiveLabel[]; const kind = node.kind; @@ -456,14 +462,14 @@ namespace ts { if (saveState) { savedHasExplicitReturn = hasExplicitReturn; savedCurrentFlow = currentFlow; - savedBreakTarget = breakTarget; - savedContinueTarget = continueTarget; + savedBreakTarget = currentBreakTarget; + savedContinueTarget = currentContinueTarget; savedActiveLabels = activeLabels; hasExplicitReturn = false; currentFlow = { kind: FlowKind.Start }; - breakTarget = undefined; - continueTarget = undefined; + currentBreakTarget = undefined; + currentContinueTarget = undefined; activeLabels = undefined; } @@ -502,11 +508,11 @@ namespace ts { node.flags = flags; if (saveState) { - activeLabels = savedActiveLabels; - continueTarget = savedContinueTarget; - breakTarget = savedBreakTarget; - currentFlow = savedCurrentFlow; hasExplicitReturn = savedHasExplicitReturn; + currentFlow = savedCurrentFlow; + currentBreakTarget = savedBreakTarget; + currentContinueTarget = savedContinueTarget; + activeLabels = savedActiveLabels; } container = saveContainer; @@ -561,6 +567,9 @@ namespace ts { case SyntaxKind.LabeledStatement: bindLabeledStatement(node); break; + case SyntaxKind.PrefixUnaryExpression: + bindPrefixUnaryExpressionFlow(node); + break; case SyntaxKind.BinaryExpression: bindBinaryExpressionFlow(node); break; @@ -613,9 +622,6 @@ namespace ts { return true; } return false; - case SyntaxKind.AmpersandAmpersandToken: - case SyntaxKind.BarBarToken: - return isNarrowingExpression(expr.left) || isNarrowingExpression(expr.right); case SyntaxKind.InstanceOfKeyword: return isNarrowingExpression(expr.left); } @@ -656,7 +662,7 @@ namespace ts { }; } - function createFlowAssignment(antecedent: FlowNode, node: BinaryExpression | VariableDeclaration | ForInStatement | ForOfStatement): FlowNode { + function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { return { kind: FlowKind.Assignment, antecedent, @@ -678,21 +684,78 @@ namespace ts { return flow; } + function isStatementCondition(node: Node) { + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.IfStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: + return (parent).expression === node; + case SyntaxKind.ForStatement: + case SyntaxKind.ConditionalExpression: + return (parent).condition === node; + } + return false; + } + + function isLogicalExpression(node: Node) { + while (true) { + if (node.kind === SyntaxKind.ParenthesizedExpression) { + node = (node).expression; + } + else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node).operator === SyntaxKind.ExclamationToken) { + node = (node).operand; + } + else { + return node.kind === SyntaxKind.BinaryExpression && ( + (node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || + (node).operatorToken.kind === SyntaxKind.BarBarToken); + } + } + } + + function isTopLevelLogicalExpression(node: Node): boolean { + while (node.parent.kind === SyntaxKind.ParenthesizedExpression || + node.parent.kind === SyntaxKind.PrefixUnaryExpression && + (node.parent).operator === SyntaxKind.ExclamationToken) { + node = node.parent; + } + return !isStatementCondition(node) && !isLogicalExpression(node.parent); + } + + function bindCondition(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) { + const saveTrueTarget = currentTrueTarget; + const saveFalseTarget = currentFalseTarget; + currentTrueTarget = trueTarget; + currentFalseTarget = falseTarget; + bind(node); + currentTrueTarget = saveTrueTarget; + currentFalseTarget = saveFalseTarget; + if (!node || !isLogicalExpression(node)) { + addAntecedent(trueTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ true)); + addAntecedent(falseTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ false)); + } + } + + function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void { + const saveBreakTarget = currentBreakTarget; + const saveContinueTarget = currentContinueTarget; + currentBreakTarget = breakTarget; + currentContinueTarget = continueTarget; + bind(node); + currentBreakTarget = saveBreakTarget; + currentContinueTarget = saveContinueTarget; + } + function bindWhileStatement(node: WhileStatement): void { const preWhileLabel = createFlowLabel(); + const preBodyLabel = createFlowLabel(); const postWhileLabel = createFlowLabel(); addAntecedent(preWhileLabel, currentFlow); currentFlow = preWhileLabel; - bind(node.expression); - addAntecedent(postWhileLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ false)); - currentFlow = createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true); - const saveBreakTarget = breakTarget; - const saveContinueTarget = continueTarget; - breakTarget = postWhileLabel; - continueTarget = preWhileLabel; - bind(node.statement); - breakTarget = saveBreakTarget; - continueTarget = saveContinueTarget; + bindCondition(node.expression, preBodyLabel, postWhileLabel); + currentFlow = finishFlow(preBodyLabel); + bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel); addAntecedent(preWhileLabel, currentFlow); currentFlow = finishFlow(postWhileLabel); } @@ -703,38 +766,24 @@ namespace ts { const postDoLabel = createFlowLabel(); addAntecedent(preDoLabel, currentFlow); currentFlow = preDoLabel; - const saveBreakTarget = breakTarget; - const saveContinueTarget = continueTarget; - breakTarget = postDoLabel; - continueTarget = preConditionLabel; - bind(node.statement); - breakTarget = saveBreakTarget; - continueTarget = saveContinueTarget; + bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); addAntecedent(preConditionLabel, currentFlow); currentFlow = finishFlow(preConditionLabel); - bind(node.expression); - addAntecedent(preDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true)); - addAntecedent(postDoLabel, createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ false)); + bindCondition(node.expression, preDoLabel, postDoLabel); currentFlow = finishFlow(postDoLabel); } function bindForStatement(node: ForStatement): void { const preLoopLabel = createFlowLabel(); + const preBodyLabel = createFlowLabel(); const postLoopLabel = createFlowLabel(); bind(node.initializer); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; - bind(node.condition); - addAntecedent(postLoopLabel, createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ false)); - currentFlow = createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ true); - const saveBreakTarget = breakTarget; - const saveContinueTarget = continueTarget; - breakTarget = postLoopLabel; - continueTarget = preLoopLabel; - bind(node.statement); + bindCondition(node.condition, preBodyLabel, postLoopLabel); + currentFlow = finishFlow(preBodyLabel); + bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); bind(node.incrementor); - breakTarget = saveBreakTarget; - continueTarget = saveContinueTarget; addAntecedent(preLoopLabel, currentFlow); currentFlow = finishFlow(postLoopLabel); } @@ -742,31 +791,28 @@ namespace ts { function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { const preLoopLabel = createFlowLabel(); const postLoopLabel = createFlowLabel(); - bind(node.initializer); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; bind(node.expression); addAntecedent(postLoopLabel, currentFlow); - const saveBreakTarget = breakTarget; - const saveContinueTarget = continueTarget; - breakTarget = postLoopLabel; - continueTarget = preLoopLabel; - currentFlow = createFlowAssignment(currentFlow, node); - bind(node.statement); - breakTarget = saveBreakTarget; - continueTarget = saveContinueTarget; + bind(node.initializer); + if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) { + bindAssignmentTargetFlow(node.initializer); + } + bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); addAntecedent(preLoopLabel, currentFlow); currentFlow = finishFlow(postLoopLabel); } function bindIfStatement(node: IfStatement): void { + const thenLabel = createFlowLabel(); + const elseLabel = createFlowLabel(); const postIfLabel = createFlowLabel(); - bind(node.expression); - const postConditionFlow = currentFlow; - currentFlow = createFlowCondition(currentFlow, node.expression, /*assumeTrue*/ true); + bindCondition(node.expression, thenLabel, elseLabel); + currentFlow = finishFlow(thenLabel); bind(node.thenStatement); addAntecedent(postIfLabel, currentFlow); - currentFlow = createFlowCondition(postConditionFlow, node.expression, /*assumeTrue*/ false); + currentFlow = finishFlow(elseLabel); bind(node.elseStatement); addAntecedent(postIfLabel, currentFlow); currentFlow = finishFlow(postIfLabel); @@ -809,7 +855,7 @@ namespace ts { } } else { - bindbreakOrContinueFlow(node, breakTarget, continueTarget); + bindbreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); } } @@ -834,9 +880,9 @@ namespace ts { function bindSwitchStatement(node: SwitchStatement): void { const postSwitchLabel = createFlowLabel(); bind(node.expression); - const saveBreakTarget = breakTarget; + const saveBreakTarget = currentBreakTarget; const savePreSwitchCaseFlow = preSwitchCaseFlow; - breakTarget = postSwitchLabel; + currentBreakTarget = postSwitchLabel; preSwitchCaseFlow = currentFlow; bind(node.caseBlock); addAntecedent(postSwitchLabel, currentFlow); @@ -844,7 +890,7 @@ namespace ts { if (!hasDefault) { addAntecedent(postSwitchLabel, preSwitchCaseFlow); } - breakTarget = saveBreakTarget; + currentBreakTarget = saveBreakTarget; preSwitchCaseFlow = savePreSwitchCaseFlow; currentFlow = finishFlow(postSwitchLabel); } @@ -904,43 +950,118 @@ namespace ts { currentFlow = finishFlow(postStatementLabel); } + function bindDestructuringTargetFlow(node: Expression) { + if (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.EqualsToken) { + bindAssignmentTargetFlow((node).left); + } + else { + bindAssignmentTargetFlow(node); + } + } + + function bindAssignmentTargetFlow(node: Expression) { + if (isNarrowableReference(node)) { + currentFlow = createFlowAssignment(currentFlow, node); + } + else if (node.kind === SyntaxKind.ArrayLiteralExpression) { + for (const e of (node).elements) { + if (e.kind === SyntaxKind.SpreadElementExpression) { + bindAssignmentTargetFlow((e).expression); + } + else { + bindDestructuringTargetFlow(e); + } + } + } + else if (node.kind === SyntaxKind.ObjectLiteralExpression) { + for (const p of (node).properties) { + if (p.kind === SyntaxKind.PropertyAssignment) { + bindDestructuringTargetFlow((p).initializer); + } + else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { + bindAssignmentTargetFlow((p).name); + } + } + } + } + + function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { + const preRightLabel = createFlowLabel(); + if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { + bindCondition(node.left, preRightLabel, falseTarget); + } + else { + bindCondition(node.left, trueTarget, preRightLabel); + } + currentFlow = finishFlow(preRightLabel); + bind(node.operatorToken); + bindCondition(node.right, trueTarget, falseTarget); + } + + function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) { + if (node.operator === SyntaxKind.ExclamationToken) { + const saveTrueTarget = currentTrueTarget; + currentTrueTarget = currentFalseTarget; + currentFalseTarget = saveTrueTarget; + forEachChild(node, bind); + currentFalseTarget = currentTrueTarget; + currentTrueTarget = saveTrueTarget; + } + else { + forEachChild(node, bind); + } + } + function bindBinaryExpressionFlow(node: BinaryExpression) { const operator = node.operatorToken.kind; if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { - const postExpressionLabel = createFlowLabel(); - bind(node.left); - bind(node.operatorToken); - addAntecedent(postExpressionLabel, currentFlow); - currentFlow = createFlowCondition(currentFlow, node.left, /*assumeTrue*/ operator === SyntaxKind.AmpersandAmpersandToken); - bind(node.right); - addAntecedent(postExpressionLabel, currentFlow); - currentFlow = finishFlow(postExpressionLabel); + if (isTopLevelLogicalExpression(node)) { + const postExpressionLabel = createFlowLabel(); + bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); + currentFlow = finishFlow(postExpressionLabel); + } + else { + bindLogicalExpression(node, currentTrueTarget, currentFalseTarget); + } } else { forEachChild(node, bind); - if (operator === SyntaxKind.EqualsToken) { - currentFlow = createFlowAssignment(currentFlow, node); + if (operator === SyntaxKind.EqualsToken && !isAssignmentTarget(node)) { + bindAssignmentTargetFlow(node.left); } } } function bindConditionalExpressionFlow(node: ConditionalExpression) { + const trueLabel = createFlowLabel(); + const falseLabel = createFlowLabel(); const postExpressionLabel = createFlowLabel(); - bind(node.condition); - const postConditionFlow = currentFlow; - currentFlow = createFlowCondition(currentFlow, node.condition, /*assumeTrue*/ true); + bindCondition(node.condition, trueLabel, falseLabel); + currentFlow = finishFlow(trueLabel); bind(node.whenTrue); addAntecedent(postExpressionLabel, currentFlow); - currentFlow = createFlowCondition(postConditionFlow, node.condition, /*assumeTrue*/ false); + currentFlow = finishFlow(falseLabel); bind(node.whenFalse); addAntecedent(postExpressionLabel, currentFlow); currentFlow = finishFlow(postExpressionLabel); } + function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) { + const name = node.name; + if (isBindingPattern(name)) { + for (const child of name.elements) { + bindInitializedVariableFlow(child); + } + } + else { + currentFlow = createFlowAssignment(currentFlow, node); + } + } + function bindVariableDeclarationFlow(node: VariableDeclaration) { forEachChild(node, bind); - if (node.initializer) { - currentFlow = createFlowAssignment(currentFlow, node); + if (node.initializer || node.parent.parent.kind === SyntaxKind.ForInStatement || node.parent.parent.kind === SyntaxKind.ForOfStatement) { + bindInitializedVariableFlow(node); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6fc60c5c78a5a..2393b23664826 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7335,6 +7335,59 @@ namespace ts { return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType; } + function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { + const type = checkExpressionCached(node.right); + const parent = node.parent; + if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.PropertyAssignment) { + const assignedType = getAssignedType(node); + return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), type]); + } + return type; + } + + function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { + const arrayLikeType = getAssignedType(node); + const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); + const propName = "" + indexOf(node.elements, element); + return isTupleLikeType(arrayLikeType) && getTypeOfPropertyOfType(arrayLikeType, propName) || elementType; + } + + function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type { + const arrayLikeType = getAssignedType(node.parent); + const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); + return createArrayType(elementType); + } + + function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment): Type { + const objectType = getAssignedType(node.parent); + const text = getTextOfPropertyName(node.name); + return getTypeOfPropertyOfType(objectType, text) || + isNumericLiteralName(text) && getIndexTypeOfType(objectType, IndexKind.Number) || + getIndexTypeOfType(objectType, IndexKind.String) || + unknownType; + } + + function getAssignedType(node: Expression): Type { + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.ForInStatement: + return stringType; + case SyntaxKind.ForOfStatement: + return checkRightHandSideOfForOf((parent).expression); + case SyntaxKind.BinaryExpression: + return getAssignedTypeOfBinaryExpression(parent); + case SyntaxKind.ArrayLiteralExpression: + return getAssignedTypeOfArrayLiteralElement(parent, node); + case SyntaxKind.SpreadElementExpression: + return getAssignedTypeOfSpreadElement(parent); + case SyntaxKind.PropertyAssignment: + return getAssignedTypeOfPropertyAssignment(parent); + case SyntaxKind.ShorthandPropertyAssignment: + break; // !!! TODO + } + return unknownType; + } + function getNarrowedTypeOfReference(type: Type, reference: Node) { if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { return type; @@ -7384,58 +7437,34 @@ namespace ts { } } - function getTypeAtVariableDeclaration(node: VariableDeclaration) { - if (reference.kind === SyntaxKind.Identifier && !isBindingPattern(node.name) && getResolvedSymbol(reference) === getSymbolOfNode(node)) { - return getAssignmentReducedType(declaredType, checkExpressionCached((node).initializer)); - } - return undefined; - } - - function getTypeAtForInOrForOfStatement(node: ForInStatement | ForOfStatement) { - if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { - if (reference.kind === SyntaxKind.Identifier) { - const variable = (node.initializer).declarations[0]; - if (variable && !isBindingPattern(variable.name) && getResolvedSymbol(reference) === getSymbolOfNode(variable)) { - return declaredType; - } - } - } - else { - if (isMatchingReference(reference, node.initializer)) { - const type = node.kind === SyntaxKind.ForOfStatement ? checkRightHandSideOfForOf(node.expression) : stringType; - return getAssignmentReducedType(declaredType, type); - } - if (reference.kind === SyntaxKind.PropertyAccessExpression && - containsMatchingReference((reference).expression, node.initializer)) { - return declaredType; + function getTypeAtVariableDeclaration(node: VariableDeclaration | BindingElement) { + if (reference.kind === SyntaxKind.Identifier && getResolvedSymbol(reference) === getSymbolOfNode(node)) { + if (node.initializer) { + return getAssignmentReducedType(declaredType, checkExpressionCached(node.initializer)); } + return declaredType; // !!! TODO } return undefined; } function getTypeAtFlowAssignment(flow: FlowAssignment) { const node = flow.node; - switch (node.kind) { - case SyntaxKind.BinaryExpression: - // If reference matches left hand side and type on right is properly assignable, - // return type on right. Otherwise default to the declared type. - if (isMatchingReference(reference, (node).left)) { - return getAssignmentReducedType(declaredType, checkExpressionCached((node).right)); - } - // We didn't have a direct match. However, if the reference is a dotted name, this - // may be an assignment to a left hand part of the reference. For example, for a - // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, - // return the declared type. - if (reference.kind === SyntaxKind.PropertyAccessExpression && - containsMatchingReference((reference).expression, (node).left)) { - return declaredType; - } - break; - case SyntaxKind.VariableDeclaration: - return getTypeAtVariableDeclaration(node); - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - return getTypeAtForInOrForOfStatement(node); + if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { + return getTypeAtVariableDeclaration(node); + } + // 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 getAssignmentReducedType(declaredType, getAssignedType(node)); + } + // We didn't have a direct match. However, if the reference is a dotted name, this + // may be an assignment to a left hand part of the reference. For example, for a + // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, + // return the declared type. + if (reference.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference((reference).expression, node)) { + return declaredType; } // Assignment doesn't affect reference return undefined; @@ -7503,10 +7532,6 @@ namespace ts { return narrowTypeByTypeof(type, expr, assumeTrue); } break; - case SyntaxKind.AmpersandAmpersandToken: - return narrowTypeByAnd(type, expr, assumeTrue); - case SyntaxKind.BarBarToken: - return narrowTypeByOr(type, expr, assumeTrue); case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); } @@ -7558,36 +7583,6 @@ namespace ts { return getTypeWithFacts(type, facts); } - function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - if (assumeTrue) { - // The assumed result is true, therefore we narrow assuming each operand to be true. - return narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true); - } - else { - // The assumed result is false. This means either the first operand was false, or the first operand was true - // and the second operand was false. We narrow with those assumptions and union the two resulting types. - return getUnionType([ - narrowType(type, expr.left, /*assumeTrue*/ false), - narrowType(type, expr.right, /*assumeTrue*/ false) - ]); - } - } - - function narrowTypeByOr(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - if (assumeTrue) { - // The assumed result is true. This means either the first operand was true, or the first operand was false - // and the second operand was true. We narrow with those assumptions and union the two resulting types. - return getUnionType([ - narrowType(type, expr.left, /*assumeTrue*/ true), - narrowType(type, expr.right, /*assumeTrue*/ true) - ]); - } - else { - // The assumed result is false, therefore we narrow assuming each operand to be false. - return narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false); - } - } - function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // Check that type is not any, assumed result is true, and we have variable symbol on the left if (isTypeAny(type) || !isMatchingReference(expr.left, reference)) { @@ -8718,32 +8713,6 @@ namespace ts { return mapper && mapper.context; } - // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property - // assignment in an object literal that is an assignment target, or if it is parented by an array literal that is - // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'. - function isAssignmentTarget(node: Node): boolean { - while (node.parent.kind === SyntaxKind.ParenthesizedExpression) { - node = node.parent; - } - while (true) { - if (node.parent.kind === SyntaxKind.PropertyAssignment) { - node = node.parent.parent; - } - else if (node.parent.kind === SyntaxKind.ArrayLiteralExpression) { - node = node.parent; - } - else { - break; - } - } - const parent = node.parent; - return parent.kind === SyntaxKind.BinaryExpression && - (parent).operatorToken.kind === SyntaxKind.EqualsToken && - (parent).left === node || - (parent.kind === SyntaxKind.ForInStatement || parent.kind === SyntaxKind.ForOfStatement) && - (parent).initializer === node; - } - function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type { // It is usually not safe to call checkExpressionCached if we can be contextually typing. // You can tell that we are contextually typing because of the contextualMapper parameter. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ed82d65d5dd01..7deee80476b78 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1537,10 +1537,10 @@ namespace ts { antecedents: FlowNode[]; } - // FlowAssignment represents a node that possibly assigns a value to one or more - // references. + // FlowAssignment represents a node that assigns a value to a narrowable reference, + // i.e. an identifier or a dotted name that starts with an identifier or 'this'. export interface FlowAssignment extends FlowNode { - node: BinaryExpression | VariableDeclaration | ForInStatement | ForOfStatement; + node: Expression | VariableDeclaration | BindingElement; antecedent: FlowNode; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index da33c18dfb812..45567538dcd24 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1316,6 +1316,31 @@ namespace ts { return !!node && (node.kind === SyntaxKind.ArrayBindingPattern || node.kind === SyntaxKind.ObjectBindingPattern); } + // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property + // assignment in an object literal that is an assignment target, or if it is parented by an array literal that is + // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'. + export function isAssignmentTarget(node: Node): boolean { + while (node.parent.kind === SyntaxKind.ParenthesizedExpression) { + node = node.parent; + } + while (true) { + const parent = node.parent; + if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.SpreadElementExpression) { + node = parent; + continue; + } + if (parent.kind === SyntaxKind.PropertyAssignment) { + node = parent.parent; + continue; + } + return parent.kind === SyntaxKind.BinaryExpression && + (parent).operatorToken.kind === SyntaxKind.EqualsToken && + (parent).left === node || + (parent.kind === SyntaxKind.ForInStatement || parent.kind === SyntaxKind.ForOfStatement) && + (parent).initializer === node; + } + } + export function isNodeDescendentOf(node: Node, ancestor: Node): boolean { while (node) { if (node === ancestor) return true; From 019f5bd4e8247464b003a124fcb74caa9f582eb1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 8 Apr 2016 09:21:17 -0700 Subject: [PATCH 23/67] Accepting new baselines --- .../reference/restElementWithAssignmentPattern5.types | 2 +- ...tionDestructuringForArrayBindingPatternDefaultValues.types | 2 +- tests/baselines/reference/typeGuardOfFormInstanceOf.types | 4 ++-- .../reference/typeGuardOfFormInstanceOfOnInterface.types | 4 ++-- tests/baselines/reference/typeGuardOfFormIsType.types | 4 ++-- .../reference/typeGuardOfFormIsTypeOnInterfaces.types | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/baselines/reference/restElementWithAssignmentPattern5.types b/tests/baselines/reference/restElementWithAssignmentPattern5.types index e3934c3119721..c5cbc9ad20626 100644 --- a/tests/baselines/reference/restElementWithAssignmentPattern5.types +++ b/tests/baselines/reference/restElementWithAssignmentPattern5.types @@ -7,7 +7,7 @@ var s: string, s2: string; >[...[s, s2]] = ["", ""] : string[] >[...[s, s2]] : string[] >...[s, s2] : string ->[s, s2] : string[] +>[s, s2] : [string, string] >s : string >s2 : string >["", ""] : string[] diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types b/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types index 245328296ebed..102e784aa914f 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types +++ b/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types @@ -595,5 +595,5 @@ for (let [numberA3 = -1, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < >console.log : (msg: any) => void >console : { log(msg: any): void; } >log : (msg: any) => void ->numberA3 : number | string +>numberA3 : number } diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOf.types b/tests/baselines/reference/typeGuardOfFormInstanceOf.types index 6b83b57dcb568..608e827f47995 100644 --- a/tests/baselines/reference/typeGuardOfFormInstanceOf.types +++ b/tests/baselines/reference/typeGuardOfFormInstanceOf.types @@ -60,7 +60,7 @@ num = ctor1 instanceof C2 && ctor1.p2; // C2 >num : number >ctor1 instanceof C2 && ctor1.p2 : number >ctor1 instanceof C2 : boolean ->ctor1 : C1 | C2 +>ctor1 : C2 | C1 >C2 : typeof C2 >ctor1.p2 : number >ctor1 : C2 @@ -109,7 +109,7 @@ num = ctor2 instanceof D1 && ctor2.p3; // D1 >num : number >ctor2 instanceof D1 && ctor2.p3 : number >ctor2 instanceof D1 : boolean ->ctor2 : C2 | D1 +>ctor2 : D1 | C2 >D1 : typeof D1 >ctor2.p3 : number >ctor2 : D1 diff --git a/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types index 20d1c1644fcd7..a5387e66d4401 100644 --- a/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types +++ b/tests/baselines/reference/typeGuardOfFormInstanceOfOnInterface.types @@ -84,7 +84,7 @@ num = c1Orc2 instanceof c2 && c1Orc2.p2; // C2 >num : number >c1Orc2 instanceof c2 && c1Orc2.p2 : number >c1Orc2 instanceof c2 : boolean ->c1Orc2 : C1 | C2 +>c1Orc2 : C2 | C1 >c2 : C2 >c1Orc2.p2 : number >c1Orc2 : C2 @@ -133,7 +133,7 @@ num = c2Ord1 instanceof d1 && c2Ord1.p3; // D1 >num : number >c2Ord1 instanceof d1 && c2Ord1.p3 : number >c2Ord1 instanceof d1 : boolean ->c2Ord1 : C2 | D1 +>c2Ord1 : D1 | C2 >d1 : D1 >c2Ord1.p3 : number >c2Ord1 : D1 diff --git a/tests/baselines/reference/typeGuardOfFormIsType.types b/tests/baselines/reference/typeGuardOfFormIsType.types index e2059be7b637e..23ce38732cdbf 100644 --- a/tests/baselines/reference/typeGuardOfFormIsType.types +++ b/tests/baselines/reference/typeGuardOfFormIsType.types @@ -80,7 +80,7 @@ num = isC2(c1Orc2) && c1Orc2.p2; // C2 >isC2(c1Orc2) && c1Orc2.p2 : number >isC2(c1Orc2) : boolean >isC2 : (x: any) => x is C2 ->c1Orc2 : C1 | C2 +>c1Orc2 : C2 | C1 >c1Orc2.p2 : number >c1Orc2 : C2 >p2 : number @@ -129,7 +129,7 @@ num = isD1(c2Ord1) && c2Ord1.p3; // D1 >isD1(c2Ord1) && c2Ord1.p3 : number >isD1(c2Ord1) : boolean >isD1 : (x: any) => x is D1 ->c2Ord1 : C2 | D1 +>c2Ord1 : D1 | C2 >c2Ord1.p3 : number >c2Ord1 : D1 >p3 : number diff --git a/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types b/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types index ea169e954138c..728d3dc0e38fb 100644 --- a/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types +++ b/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types @@ -111,7 +111,7 @@ num = isC2(c1Orc2) && c1Orc2.p2; // C2 >isC2(c1Orc2) && c1Orc2.p2 : number >isC2(c1Orc2) : boolean >isC2 : (x: any) => x is C2 ->c1Orc2 : C1 | C2 +>c1Orc2 : C2 | C1 >c1Orc2.p2 : number >c1Orc2 : C2 >p2 : number @@ -160,7 +160,7 @@ num = isD1(c2Ord1) && c2Ord1.p3; // D1 >isD1(c2Ord1) && c2Ord1.p3 : number >isD1(c2Ord1) : boolean >isD1 : (x: any) => x is D1 ->c2Ord1 : C2 | D1 +>c2Ord1 : D1 | C2 >c2Ord1.p3 : number >c2Ord1 : D1 >p3 : number From f13c92f0366acb29c01917bd1ddc18bedf428fda Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 8 Apr 2016 15:08:22 -0700 Subject: [PATCH 24/67] Handle shorthand property assignments --- src/compiler/checker.ts | 13 +++++++++++-- src/compiler/utilities.ts | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2393b23664826..918acb410aaff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7358,7 +7358,7 @@ namespace ts { return createArrayType(elementType); } - function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment): Type { + function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { const objectType = getAssignedType(node.parent); const text = getTextOfPropertyName(node.name); return getTypeOfPropertyOfType(objectType, text) || @@ -7367,6 +7367,15 @@ namespace ts { unknownType; } + function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type { + if (node.objectAssignmentInitializer) { + const defaultType = checkExpressionCached(node.objectAssignmentInitializer); + const assignedType = getAssignedTypeOfPropertyAssignment(node); + return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), defaultType]); + } + return getAssignedTypeOfPropertyAssignment(node); + } + function getAssignedType(node: Expression): Type { const parent = node.parent; switch (parent.kind) { @@ -7383,7 +7392,7 @@ namespace ts { case SyntaxKind.PropertyAssignment: return getAssignedTypeOfPropertyAssignment(parent); case SyntaxKind.ShorthandPropertyAssignment: - break; // !!! TODO + return getAssignedTypeOfShorthandPropertyAssignment(parent); } return unknownType; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 45567538dcd24..f6a4b81e6d2c7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1329,7 +1329,7 @@ namespace ts { node = parent; continue; } - if (parent.kind === SyntaxKind.PropertyAssignment) { + if (parent.kind === SyntaxKind.PropertyAssignment || parent.kind === SyntaxKind.ShorthandPropertyAssignment) { node = parent.parent; continue; } From b03d087e79f832959b8d764547d8c1fdc4c6cdc6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 8 Apr 2016 15:10:10 -0700 Subject: [PATCH 25/67] Accepting new baselines --- .../reference/assignmentTypeNarrowing.js | 53 ++++++++++ .../reference/assignmentTypeNarrowing.symbols | 64 ++++++++++++ .../reference/assignmentTypeNarrowing.types | 98 +++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.js create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.symbols create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.types diff --git a/tests/baselines/reference/assignmentTypeNarrowing.js b/tests/baselines/reference/assignmentTypeNarrowing.js new file mode 100644 index 0000000000000..7c10dde65ccac --- /dev/null +++ b/tests/baselines/reference/assignmentTypeNarrowing.js @@ -0,0 +1,53 @@ +//// [assignmentTypeNarrowing.ts] +let x: string | number | boolean | RegExp; + +x = ""; +x; // string + +[x] = [true]; +x; // boolean + +[x = ""] = [1]; +x; // string | number + +({x} = {x: true}); +x; // boolean + +({y: x} = {y: 1}); +x; // number + +({x = ""} = {x: true}); +x; // string | boolean + +({y: x = /a/} = {y: 1}); +x; // number | RegExp + +let a: string[]; + +for (x of a) { + x; // string +} + + +//// [assignmentTypeNarrowing.js] +var x; +x = ""; +x; // string +x = [true][0]; +x; // boolean +_a = [1][0], x = _a === void 0 ? "" : _a; +x; // string | number +(_b = { x: true }, x = _b.x, _b); +x; // boolean +(_c = { y: 1 }, x = _c.y, _c); +x; // number +(_d = { x: true }, _e = _d.x, x = _e === void 0 ? "" : _e, _d); +x; // string | boolean +(_f = { y: 1 }, _g = _f.y, x = _g === void 0 ? /a/ : _g, _f); +x; // number | RegExp +var a; +for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { + x = a_1[_i]; + x; // string +} +var _a, _b, _c, _d, _e, _f, _g; diff --git a/tests/baselines/reference/assignmentTypeNarrowing.symbols b/tests/baselines/reference/assignmentTypeNarrowing.symbols new file mode 100644 index 0000000000000..7d638d6a76bfa --- /dev/null +++ b/tests/baselines/reference/assignmentTypeNarrowing.symbols @@ -0,0 +1,64 @@ +=== tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts === +let x: string | number | boolean | RegExp; +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +x = ""; +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +x; // string +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +[x] = [true]; +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +x; // boolean +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +[x = ""] = [1]; +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +x; // string | number +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +({x} = {x: true}); +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 11, 2)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 11, 8)) + +x; // boolean +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +({y: x} = {y: 1}); +>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 14, 2)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) +>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 14, 11)) + +x; // number +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +({x = ""} = {x: true}); +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 17, 2)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 17, 13)) + +x; // string | boolean +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +({y: x = /a/} = {y: 1}); +>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 20, 2)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) +>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 20, 17)) + +x; // number | RegExp +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) + +let a: string[]; +>a : Symbol(a, Decl(assignmentTypeNarrowing.ts, 23, 3)) + +for (x of a) { +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) +>a : Symbol(a, Decl(assignmentTypeNarrowing.ts, 23, 3)) + + x; // string +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) +} + diff --git a/tests/baselines/reference/assignmentTypeNarrowing.types b/tests/baselines/reference/assignmentTypeNarrowing.types new file mode 100644 index 0000000000000..be99a31501d06 --- /dev/null +++ b/tests/baselines/reference/assignmentTypeNarrowing.types @@ -0,0 +1,98 @@ +=== tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts === +let x: string | number | boolean | RegExp; +>x : string | number | boolean | RegExp +>RegExp : RegExp + +x = ""; +>x = "" : string +>x : string | number | boolean | RegExp +>"" : string + +x; // string +>x : string + +[x] = [true]; +>[x] = [true] : [boolean] +>[x] : [string | number | boolean | RegExp] +>x : string | number | boolean | RegExp +>[true] : [boolean] +>true : boolean + +x; // boolean +>x : boolean + +[x = ""] = [1]; +>[x = ""] = [1] : [number] +>[x = ""] : [string] +>x = "" : string +>x : string | number | boolean | RegExp +>"" : string +>[1] : [number] +>1 : number + +x; // string | number +>x : string | number + +({x} = {x: true}); +>({x} = {x: true}) : { x: boolean; } +>{x} = {x: true} : { x: boolean; } +>{x} : { x: string | number | boolean | RegExp; } +>x : string | number | boolean | RegExp +>{x: true} : { x: boolean; } +>x : boolean +>true : boolean + +x; // boolean +>x : boolean + +({y: x} = {y: 1}); +>({y: x} = {y: 1}) : { y: number; } +>{y: x} = {y: 1} : { y: number; } +>{y: x} : { y: string | number | boolean | RegExp; } +>y : string | number | boolean | RegExp +>x : string | number | boolean | RegExp +>{y: 1} : { y: number; } +>y : number +>1 : number + +x; // number +>x : number + +({x = ""} = {x: true}); +>({x = ""} = {x: true}) : { x?: boolean; } +>{x = ""} = {x: true} : { x?: boolean; } +>{x = ""} : { x?: string | number | boolean | RegExp; } +>x : string | number | boolean | RegExp +>{x: true} : { x?: boolean; } +>x : boolean +>true : boolean + +x; // string | boolean +>x : string | boolean + +({y: x = /a/} = {y: 1}); +>({y: x = /a/} = {y: 1}) : { y?: number; } +>{y: x = /a/} = {y: 1} : { y?: number; } +>{y: x = /a/} : { y?: RegExp; } +>y : RegExp +>x = /a/ : RegExp +>x : string | number | boolean | RegExp +>/a/ : RegExp +>{y: 1} : { y?: number; } +>y : number +>1 : number + +x; // number | RegExp +>x : number | RegExp + +let a: string[]; +>a : string[] + +for (x of a) { +>x : string | number | boolean | RegExp +>a : string[] + + x; // string +>x : string +} + From 7a321293bf43de3ac4ffbdf1281eb5886b623341 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 16:56:23 -0700 Subject: [PATCH 26/67] Support destructuring declarations in control flow analysis --- src/compiler/checker.ts | 113 +++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 918acb410aaff..e12f65b72911c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7335,45 +7335,52 @@ namespace ts { return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType; } - function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { - const type = checkExpressionCached(node.right); - const parent = node.parent; - if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.PropertyAssignment) { - const assignedType = getAssignedType(node); - return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), type]); + function getTypeWithDefault(type: Type, defaultExpression: Expression) { + if (defaultExpression) { + const defaultType = checkExpressionCached(defaultExpression); + return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]); } return type; } + function getTypeOfDestructuredProperty(type: Type, name: Identifier | LiteralExpression | ComputedPropertyName) { + const text = getTextOfPropertyName(name); + return getTypeOfPropertyOfType(type, text) || + isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) || + getIndexTypeOfType(type, IndexKind.String) || + unknownType; + } + + function getTypeOfDestructuredArrayElement(type: Type, index: number) { + return isTupleLikeType(type) && getTypeOfPropertyOfType(type, "" + index) || + checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || + unknownType; + } + + function getTypeOfDestructuredSpreadElement(type: Type) { + return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || unknownType); + } + + function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { + return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ? + getTypeWithDefault(getAssignedType(node), node.right) : + checkExpressionCached(node.right); + } + function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { - const arrayLikeType = getAssignedType(node); - const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); - const propName = "" + indexOf(node.elements, element); - return isTupleLikeType(arrayLikeType) && getTypeOfPropertyOfType(arrayLikeType, propName) || elementType; + return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element)); } function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type { - const arrayLikeType = getAssignedType(node.parent); - const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false); - return createArrayType(elementType); + return getTypeOfDestructuredSpreadElement(getAssignedType(node.parent)); } function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { - const objectType = getAssignedType(node.parent); - const text = getTextOfPropertyName(node.name); - return getTypeOfPropertyOfType(objectType, text) || - isNumericLiteralName(text) && getIndexTypeOfType(objectType, IndexKind.Number) || - getIndexTypeOfType(objectType, IndexKind.String) || - unknownType; + return getTypeOfDestructuredProperty(getAssignedType(node.parent), node.name); } function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type { - if (node.objectAssignmentInitializer) { - const defaultType = checkExpressionCached(node.objectAssignmentInitializer); - const assignedType = getAssignedTypeOfPropertyAssignment(node); - return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), defaultType]); - } - return getAssignedTypeOfPropertyAssignment(node); + return getTypeWithDefault(getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer); } function getAssignedType(node: Expression): Type { @@ -7382,7 +7389,7 @@ namespace ts { case SyntaxKind.ForInStatement: return stringType; case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf((parent).expression); + return checkRightHandSideOfForOf((parent).expression) || unknownType; case SyntaxKind.BinaryExpression: return getAssignedTypeOfBinaryExpression(parent); case SyntaxKind.ArrayLiteralExpression: @@ -7397,6 +7404,36 @@ namespace ts { return unknownType; } + function getInitialTypeOfBindingElement(node: BindingElement): Type { + const pattern = node.parent; + const parentType = getInitialType(pattern.parent); + const type = pattern.kind === SyntaxKind.ObjectBindingPattern ? + getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : + !node.dotDotDotToken ? + getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) : + getTypeOfDestructuredSpreadElement(parentType); + return getTypeWithDefault(type, node.initializer); + } + + function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { + if (node.initializer) { + return checkExpressionCached(node.initializer); + } + if (node.parent.parent.kind === SyntaxKind.ForInStatement) { + return stringType; + } + if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { + return checkRightHandSideOfForOf((node.parent.parent).expression) || unknownType; + } + return unknownType; + } + + function getInitialType(node: VariableDeclaration | BindingElement) { + return node.kind === SyntaxKind.VariableDeclaration ? + getInitialTypeOfVariableDeclaration(node) : + getInitialTypeOfBindingElement(node); + } + function getNarrowedTypeOfReference(type: Type, reference: Node) { if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { return type; @@ -7446,20 +7483,12 @@ namespace ts { } } - function getTypeAtVariableDeclaration(node: VariableDeclaration | BindingElement) { - if (reference.kind === SyntaxKind.Identifier && getResolvedSymbol(reference) === getSymbolOfNode(node)) { - if (node.initializer) { - return getAssignmentReducedType(declaredType, checkExpressionCached(node.initializer)); - } - return declaredType; // !!! TODO - } - return undefined; - } - function getTypeAtFlowAssignment(flow: FlowAssignment) { const node = flow.node; - if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { - return getTypeAtVariableDeclaration(node); + if ((node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) && + reference.kind === SyntaxKind.Identifier && + getResolvedSymbol(reference) === getSymbolOfNode(node)) { + return getAssignmentReducedType(declaredType, getInitialType(node)); } // 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 @@ -14091,23 +14120,21 @@ namespace ts { if (isTypeAny(inputType)) { return inputType; } - if (languageVersion >= ScriptTarget.ES6) { return checkElementTypeOfIterable(inputType, errorNode); } - if (allowStringInput) { return checkElementTypeOfArrayOrString(inputType, errorNode); } - if (isArrayLikeType(inputType)) { const indexType = getIndexTypeOfType(inputType, IndexKind.Number); if (indexType) { return indexType; } } - - error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + if (errorNode) { + error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + } return unknownType; } From 6fb9424b8444bcb4a5585aa16ccd997bcf371e0e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 16:57:05 -0700 Subject: [PATCH 27/67] Accepting new baselines --- ...dationDestructuringForArrayBindingPatternDefaultValues.types | 2 +- tests/baselines/reference/stringLiteralTypesAndTuples01.types | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types b/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types index 102e784aa914f..245328296ebed 100644 --- a/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types +++ b/tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types @@ -595,5 +595,5 @@ for (let [numberA3 = -1, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < >console.log : (msg: any) => void >console : { log(msg: any): void; } >log : (msg: any) => void ->numberA3 : number +>numberA3 : number | string } diff --git a/tests/baselines/reference/stringLiteralTypesAndTuples01.types b/tests/baselines/reference/stringLiteralTypesAndTuples01.types index c874973e3e590..e8b5b37a2dda8 100644 --- a/tests/baselines/reference/stringLiteralTypesAndTuples01.types +++ b/tests/baselines/reference/stringLiteralTypesAndTuples01.types @@ -28,7 +28,7 @@ let [im, a, dinosaur]: ["I'm", "a", RexOrRaptor] = ['I\'m', 'a', 't-rex']; rawr(dinosaur); >rawr(dinosaur) : string >rawr : (dino: "t-rex" | "raptor") => string ->dinosaur : "t-rex" | "raptor" +>dinosaur : "t-rex" function rawr(dino: RexOrRaptor) { >rawr : (dino: "t-rex" | "raptor") => string From e45bac813954c72494ecd2cb5672ed51858c3a83 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 17:56:03 -0700 Subject: [PATCH 28/67] Adding test --- .../controlFlowDestructuringDeclaration.js | 113 ++++++++++ ...ontrolFlowDestructuringDeclaration.symbols | 164 +++++++++++++++ .../controlFlowDestructuringDeclaration.types | 196 ++++++++++++++++++ .../controlFlowDestructuringDeclaration.ts | 59 ++++++ 4 files changed, 532 insertions(+) create mode 100644 tests/baselines/reference/controlFlowDestructuringDeclaration.js create mode 100644 tests/baselines/reference/controlFlowDestructuringDeclaration.symbols create mode 100644 tests/baselines/reference/controlFlowDestructuringDeclaration.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts diff --git a/tests/baselines/reference/controlFlowDestructuringDeclaration.js b/tests/baselines/reference/controlFlowDestructuringDeclaration.js new file mode 100644 index 0000000000000..766fb6baeb298 --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringDeclaration.js @@ -0,0 +1,113 @@ +//// [controlFlowDestructuringDeclaration.ts] + +function f1() { + let x: string | number = 1; + x; + let y: string | undefined = ""; + y; +} + +function f2() { + let [x]: [string | number] = [1]; + x; + let [y]: [string | undefined] = [""]; + y; + let [z = ""]: [string | undefined] = [undefined]; + z; +} + +function f3() { + let [x]: (string | number)[] = [1]; + x; + let [y]: (string | undefined)[] = [""]; + y; + let [z = ""]: (string | undefined)[] = [undefined]; + z; +} + +function f4() { + let { x }: { x: string | number } = { x: 1 }; + x; + let { y }: { y: string | undefined } = { y: "" }; + y; + let { z = "" }: { z: string | undefined } = { z: undefined }; + z; +} + +function f5() { + let { x }: { x?: string | number } = { x: 1 }; + x; + let { y }: { y?: string | undefined } = { y: "" }; + y; + let { z = "" }: { z?: string | undefined } = { z: undefined }; + z; +} + +function f6() { + let { x }: { x?: string | number } = {}; + x; + let { y }: { y?: string | undefined } = {}; + y; + let { z = "" }: { z?: string | undefined } = {}; + z; +} + +function f7() { + let o: { [x: string]: number } = { x: 1 }; + let { x }: { [x: string]: string | number } = o; + x; +} + + +//// [controlFlowDestructuringDeclaration.js] +function f1() { + var x = 1; + x; + var y = ""; + y; +} +function f2() { + var x = [1][0]; + x; + var y = [""][0]; + y; + var _a = [undefined][0], z = _a === void 0 ? "" : _a; + z; +} +function f3() { + var x = [1][0]; + x; + var y = [""][0]; + y; + var _a = [undefined][0], z = _a === void 0 ? "" : _a; + z; +} +function f4() { + var x = { x: 1 }.x; + x; + var y = { y: "" }.y; + y; + var _a = { z: undefined }.z, z = _a === void 0 ? "" : _a; + z; +} +function f5() { + var x = { x: 1 }.x; + x; + var y = { y: "" }.y; + y; + var _a = { z: undefined }.z, z = _a === void 0 ? "" : _a; + z; +} +function f6() { + var x = {}.x; + x; + var y = {}.y; + y; + var _a = {}.z, z = _a === void 0 ? "" : _a; + z; +} +function f7() { + var o = { x: 1 }; + var x = o.x; + x; +} diff --git a/tests/baselines/reference/controlFlowDestructuringDeclaration.symbols b/tests/baselines/reference/controlFlowDestructuringDeclaration.symbols new file mode 100644 index 0000000000000..65f0e497942d7 --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringDeclaration.symbols @@ -0,0 +1,164 @@ +=== tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts === + +function f1() { +>f1 : Symbol(f1, Decl(controlFlowDestructuringDeclaration.ts, 0, 0)) + + let x: string | number = 1; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 2, 7)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 2, 7)) + + let y: string | undefined = ""; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 4, 7)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 4, 7)) +} + +function f2() { +>f2 : Symbol(f2, Decl(controlFlowDestructuringDeclaration.ts, 6, 1)) + + let [x]: [string | number] = [1]; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 9, 9)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 9, 9)) + + let [y]: [string | undefined] = [""]; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 11, 9)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 11, 9)) + + let [z = ""]: [string | undefined] = [undefined]; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 13, 9)) +>undefined : Symbol(undefined) + + z; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 13, 9)) +} + +function f3() { +>f3 : Symbol(f3, Decl(controlFlowDestructuringDeclaration.ts, 15, 1)) + + let [x]: (string | number)[] = [1]; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 18, 9)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 18, 9)) + + let [y]: (string | undefined)[] = [""]; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 20, 9)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 20, 9)) + + let [z = ""]: (string | undefined)[] = [undefined]; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 22, 9)) +>undefined : Symbol(undefined) + + z; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 22, 9)) +} + +function f4() { +>f4 : Symbol(f4, Decl(controlFlowDestructuringDeclaration.ts, 24, 1)) + + let { x }: { x: string | number } = { x: 1 }; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 27, 9)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 27, 16)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 27, 41)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 27, 9)) + + let { y }: { y: string | undefined } = { y: "" }; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 29, 9)) +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 29, 16)) +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 29, 44)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 29, 9)) + + let { z = "" }: { z: string | undefined } = { z: undefined }; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 31, 9)) +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 31, 21)) +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 31, 49)) +>undefined : Symbol(undefined) + + z; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 31, 9)) +} + +function f5() { +>f5 : Symbol(f5, Decl(controlFlowDestructuringDeclaration.ts, 33, 1)) + + let { x }: { x?: string | number } = { x: 1 }; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 36, 9)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 36, 16)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 36, 42)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 36, 9)) + + let { y }: { y?: string | undefined } = { y: "" }; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 38, 9)) +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 38, 16)) +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 38, 45)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 38, 9)) + + let { z = "" }: { z?: string | undefined } = { z: undefined }; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 40, 9)) +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 40, 21)) +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 40, 50)) +>undefined : Symbol(undefined) + + z; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 40, 9)) +} + +function f6() { +>f6 : Symbol(f6, Decl(controlFlowDestructuringDeclaration.ts, 42, 1)) + + let { x }: { x?: string | number } = {}; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 45, 9)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 45, 16)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 45, 9)) + + let { y }: { y?: string | undefined } = {}; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 47, 9)) +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 47, 16)) + + y; +>y : Symbol(y, Decl(controlFlowDestructuringDeclaration.ts, 47, 9)) + + let { z = "" }: { z?: string | undefined } = {}; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 49, 9)) +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 49, 21)) + + z; +>z : Symbol(z, Decl(controlFlowDestructuringDeclaration.ts, 49, 9)) +} + +function f7() { +>f7 : Symbol(f7, Decl(controlFlowDestructuringDeclaration.ts, 51, 1)) + + let o: { [x: string]: number } = { x: 1 }; +>o : Symbol(o, Decl(controlFlowDestructuringDeclaration.ts, 54, 7)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 54, 14)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 54, 38)) + + let { x }: { [x: string]: string | number } = o; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 55, 9)) +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 55, 18)) +>o : Symbol(o, Decl(controlFlowDestructuringDeclaration.ts, 54, 7)) + + x; +>x : Symbol(x, Decl(controlFlowDestructuringDeclaration.ts, 55, 9)) +} + diff --git a/tests/baselines/reference/controlFlowDestructuringDeclaration.types b/tests/baselines/reference/controlFlowDestructuringDeclaration.types new file mode 100644 index 0000000000000..2e3b1f5647e50 --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringDeclaration.types @@ -0,0 +1,196 @@ +=== tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts === + +function f1() { +>f1 : () => void + + let x: string | number = 1; +>x : string | number +>1 : number + + x; +>x : number + + let y: string | undefined = ""; +>y : string | undefined +>"" : string + + y; +>y : string +} + +function f2() { +>f2 : () => void + + let [x]: [string | number] = [1]; +>x : string | number +>[1] : [number] +>1 : number + + x; +>x : number + + let [y]: [string | undefined] = [""]; +>y : string | undefined +>[""] : [string] +>"" : string + + y; +>y : string + + let [z = ""]: [string | undefined] = [undefined]; +>z : string +>"" : string +>[undefined] : [undefined] +>undefined : undefined + + z; +>z : string +} + +function f3() { +>f3 : () => void + + let [x]: (string | number)[] = [1]; +>x : string | number +>[1] : number[] +>1 : number + + x; +>x : number + + let [y]: (string | undefined)[] = [""]; +>y : string | undefined +>[""] : string[] +>"" : string + + y; +>y : string + + let [z = ""]: (string | undefined)[] = [undefined]; +>z : string +>"" : string +>[undefined] : undefined[] +>undefined : undefined + + z; +>z : string +} + +function f4() { +>f4 : () => void + + let { x }: { x: string | number } = { x: 1 }; +>x : string | number +>x : string | number +>{ x: 1 } : { x: number; } +>x : number +>1 : number + + x; +>x : number + + let { y }: { y: string | undefined } = { y: "" }; +>y : string | undefined +>y : string | undefined +>{ y: "" } : { y: string; } +>y : string +>"" : string + + y; +>y : string + + let { z = "" }: { z: string | undefined } = { z: undefined }; +>z : string +>"" : string +>z : string | undefined +>{ z: undefined } : { z: undefined; } +>z : undefined +>undefined : undefined + + z; +>z : string +} + +function f5() { +>f5 : () => void + + let { x }: { x?: string | number } = { x: 1 }; +>x : string | number | undefined +>x : string | number | undefined +>{ x: 1 } : { x: number; } +>x : number +>1 : number + + x; +>x : number + + let { y }: { y?: string | undefined } = { y: "" }; +>y : string | undefined +>y : string | undefined +>{ y: "" } : { y: string; } +>y : string +>"" : string + + y; +>y : string + + let { z = "" }: { z?: string | undefined } = { z: undefined }; +>z : string +>"" : string +>z : string | undefined +>{ z: undefined } : { z: undefined; } +>z : undefined +>undefined : undefined + + z; +>z : string +} + +function f6() { +>f6 : () => void + + let { x }: { x?: string | number } = {}; +>x : string | number | undefined +>x : string | number | undefined +>{} : {} + + x; +>x : string | number | undefined + + let { y }: { y?: string | undefined } = {}; +>y : string | undefined +>y : string | undefined +>{} : {} + + y; +>y : string | undefined + + let { z = "" }: { z?: string | undefined } = {}; +>z : string +>"" : string +>z : string | undefined +>{} : {} + + z; +>z : string +} + +function f7() { +>f7 : () => void + + let o: { [x: string]: number } = { x: 1 }; +>o : { [x: string]: number; } +>x : string +>{ x: 1 } : { x: number; } +>x : number +>1 : number + + let { x }: { [x: string]: string | number } = o; +>x : string | number +>x : string +>o : { [x: string]: number; } + + x; +>x : number +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts b/tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts new file mode 100644 index 0000000000000..fc9c3ffb4d77b --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowDestructuringDeclaration.ts @@ -0,0 +1,59 @@ +// @strictNullChecks: true + +function f1() { + let x: string | number = 1; + x; + let y: string | undefined = ""; + y; +} + +function f2() { + let [x]: [string | number] = [1]; + x; + let [y]: [string | undefined] = [""]; + y; + let [z = ""]: [string | undefined] = [undefined]; + z; +} + +function f3() { + let [x]: (string | number)[] = [1]; + x; + let [y]: (string | undefined)[] = [""]; + y; + let [z = ""]: (string | undefined)[] = [undefined]; + z; +} + +function f4() { + let { x }: { x: string | number } = { x: 1 }; + x; + let { y }: { y: string | undefined } = { y: "" }; + y; + let { z = "" }: { z: string | undefined } = { z: undefined }; + z; +} + +function f5() { + let { x }: { x?: string | number } = { x: 1 }; + x; + let { y }: { y?: string | undefined } = { y: "" }; + y; + let { z = "" }: { z?: string | undefined } = { z: undefined }; + z; +} + +function f6() { + let { x }: { x?: string | number } = {}; + x; + let { y }: { y?: string | undefined } = {}; + y; + let { z = "" }: { z?: string | undefined } = {}; + z; +} + +function f7() { + let o: { [x: string]: number } = { x: 1 }; + let { x }: { [x: string]: string | number } = o; + x; +} From 7dfcad65b4fe2a76f009a1563337e0c90390ab70 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 17:56:33 -0700 Subject: [PATCH 29/67] Fixing fourslash tests --- tests/cases/fourslash/completionEntryOnNarrowedType.ts | 4 ++-- tests/cases/fourslash/shims-pp/getCompletionsAtPosition.ts | 4 ++-- tests/cases/fourslash/shims/getCompletionsAtPosition.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cases/fourslash/completionEntryOnNarrowedType.ts b/tests/cases/fourslash/completionEntryOnNarrowedType.ts index 714d3390e760b..f81572ccebf4a 100644 --- a/tests/cases/fourslash/completionEntryOnNarrowedType.ts +++ b/tests/cases/fourslash/completionEntryOnNarrowedType.ts @@ -3,10 +3,10 @@ ////function foo(strOrNum: string | number) { //// /*1*/ //// if (typeof strOrNum === "number") { -//// /*2*/ +//// strOrNum/*2*/; //// } //// else { -//// /*3*/ +//// strOrNum/*3*/; //// } ////} diff --git a/tests/cases/fourslash/shims-pp/getCompletionsAtPosition.ts b/tests/cases/fourslash/shims-pp/getCompletionsAtPosition.ts index 714d3390e760b..f81572ccebf4a 100644 --- a/tests/cases/fourslash/shims-pp/getCompletionsAtPosition.ts +++ b/tests/cases/fourslash/shims-pp/getCompletionsAtPosition.ts @@ -3,10 +3,10 @@ ////function foo(strOrNum: string | number) { //// /*1*/ //// if (typeof strOrNum === "number") { -//// /*2*/ +//// strOrNum/*2*/; //// } //// else { -//// /*3*/ +//// strOrNum/*3*/; //// } ////} diff --git a/tests/cases/fourslash/shims/getCompletionsAtPosition.ts b/tests/cases/fourslash/shims/getCompletionsAtPosition.ts index 714d3390e760b..f81572ccebf4a 100644 --- a/tests/cases/fourslash/shims/getCompletionsAtPosition.ts +++ b/tests/cases/fourslash/shims/getCompletionsAtPosition.ts @@ -3,10 +3,10 @@ ////function foo(strOrNum: string | number) { //// /*1*/ //// if (typeof strOrNum === "number") { -//// /*2*/ +//// strOrNum/*2*/; //// } //// else { -//// /*3*/ +//// strOrNum/*3*/; //// } ////} From 92df0297c80c7bd7509ecf2a503d7f98c9a23431 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 18:31:19 -0700 Subject: [PATCH 30/67] Fixing tests --- .../typeGuards/typeGuardFunctionOfFormThis.ts | 26 +++++++++---------- .../thisPredicateFunctionCompletions03.ts | 24 ----------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts index aadbf3cd9c584..12f6687c4015a 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts @@ -34,19 +34,19 @@ else if (b.isFollower()) { b.follow(); } -if (((a.isLeader)())) { - a.lead(); -} -else if (((a).isFollower())) { - a.follow(); -} - -if (((a["isLeader"])())) { - a.lead(); -} -else if (((a)["isFollower"]())) { - a.follow(); -} +// if (((a.isLeader)())) { +// a.lead(); +// } +// else if (((a).isFollower())) { +// a.follow(); +// } + +// if (((a["isLeader"])())) { +// a.lead(); +// } +// else if (((a)["isFollower"]())) { +// a.follow(); +// } var holder2 = {a}; diff --git a/tests/cases/fourslash/thisPredicateFunctionCompletions03.ts b/tests/cases/fourslash/thisPredicateFunctionCompletions03.ts index 3c93a859bf3b3..4985a1ca5613f 100644 --- a/tests/cases/fourslash/thisPredicateFunctionCompletions03.ts +++ b/tests/cases/fourslash/thisPredicateFunctionCompletions03.ts @@ -38,20 +38,6 @@ //// b./*8*/; //// } //// -//// if (((a.isLeader)())) { -//// a./*9*/; -//// } -//// else if (((a).isFollower())) { -//// a./*10*/; -//// } -//// -//// if (((a["isLeader"])())) { -//// a./*11*/; -//// } -//// else if (((a)["isFollower"]())) { -//// a./*12*/; -//// } -//// //// let leader/*13*/Status = a.isLeader(); //// function isLeaderGuard(g: RoyalGuard) { //// return g.isLeader(); @@ -68,13 +54,3 @@ goTo.marker("6"); verify.completionListContains("lead"); goTo.marker("8"); verify.completionListContains("follow"); - -goTo.marker("9"); -verify.completionListContains("lead"); -goTo.marker("10"); -verify.completionListContains("follow"); - -goTo.marker("11"); -verify.completionListContains("lead"); -goTo.marker("12"); -verify.completionListContains("follow"); \ No newline at end of file From 32e64640d585d0250293a7198cd2e98866cedd61 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 18:31:32 -0700 Subject: [PATCH 31/67] Accepting new baselines --- .../reference/typeGuardFunctionOfFormThis.js | 48 ++++++------- .../typeGuardFunctionOfFormThis.symbols | 52 ++++---------- .../typeGuardFunctionOfFormThis.types | 70 ++++--------------- 3 files changed, 50 insertions(+), 120 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js index 0d1dceccede6a..ad85f679e0ab2 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -34,19 +34,19 @@ else if (b.isFollower()) { b.follow(); } -if (((a.isLeader)())) { - a.lead(); -} -else if (((a).isFollower())) { - a.follow(); -} +// if (((a.isLeader)())) { +// a.lead(); +// } +// else if (((a).isFollower())) { +// a.follow(); +// } -if (((a["isLeader"])())) { - a.lead(); -} -else if (((a)["isFollower"]())) { - a.follow(); -} +// if (((a["isLeader"])())) { +// a.lead(); +// } +// else if (((a)["isFollower"]())) { +// a.follow(); +// } var holder2 = {a}; @@ -190,18 +190,18 @@ if (b.isLeader()) { else if (b.isFollower()) { b.follow(); } -if (((a.isLeader)())) { - a.lead(); -} -else if (((a).isFollower())) { - a.follow(); -} -if (((a["isLeader"])())) { - a.lead(); -} -else if (((a)["isFollower"]())) { - a.follow(); -} +// if (((a.isLeader)())) { +// a.lead(); +// } +// else if (((a).isFollower())) { +// a.follow(); +// } +// if (((a["isLeader"])())) { +// a.lead(); +// } +// else if (((a)["isFollower"]())) { +// a.follow(); +// } var holder2 = { a: a }; if (holder2.a.isLeader()) { holder2.a; diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols index 4f08201ba6433..3ac972200e26d 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols @@ -91,45 +91,19 @@ else if (b.isFollower()) { >follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) } -if (((a.isLeader)())) { ->a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) - - a.lead(); ->a.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) -} -else if (((a).isFollower())) { ->(a).isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) - - a.follow(); ->a.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) -} - -if (((a["isLeader"])())) { ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->"isLeader" : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) - - a.lead(); ->a.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) -} -else if (((a)["isFollower"]())) { ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->"isFollower" : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) - - a.follow(); ->a.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) ->a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) ->follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) -} +// if (((a.isLeader)())) { +// a.lead(); +// } +// else if (((a).isFollower())) { +// a.follow(); +// } + +// if (((a["isLeader"])())) { +// a.lead(); +// } +// else if (((a)["isFollower"]())) { +// a.follow(); +// } var holder2 = {a}; >holder2 : Symbol(holder2, Decl(typeGuardFunctionOfFormThis.ts, 49, 3)) diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.types b/tests/baselines/reference/typeGuardFunctionOfFormThis.types index cd0381a94e9da..66d1a4b5b11bb 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.types +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -102,63 +102,19 @@ else if (b.isFollower()) { >follow : () => void } -if (((a.isLeader)())) { ->((a.isLeader)()) : boolean ->(a.isLeader)() : boolean ->(a.isLeader) : () => this is LeadGuard ->a.isLeader : () => this is LeadGuard ->a : RoyalGuard ->isLeader : () => this is LeadGuard - - a.lead(); ->a.lead() : void ->a.lead : () => void ->a : LeadGuard ->lead : () => void -} -else if (((a).isFollower())) { ->((a).isFollower()) : boolean ->(a).isFollower() : boolean ->(a).isFollower : () => this is FollowerGuard ->(a) : RoyalGuard ->a : RoyalGuard ->isFollower : () => this is FollowerGuard - - a.follow(); ->a.follow() : void ->a.follow : () => void ->a : FollowerGuard ->follow : () => void -} - -if (((a["isLeader"])())) { ->((a["isLeader"])()) : boolean ->(a["isLeader"])() : boolean ->(a["isLeader"]) : () => this is LeadGuard ->a["isLeader"] : () => this is LeadGuard ->a : RoyalGuard ->"isLeader" : string - - a.lead(); ->a.lead() : void ->a.lead : () => void ->a : LeadGuard ->lead : () => void -} -else if (((a)["isFollower"]())) { ->((a)["isFollower"]()) : boolean ->(a)["isFollower"]() : boolean ->(a)["isFollower"] : () => this is FollowerGuard ->(a) : RoyalGuard ->a : RoyalGuard ->"isFollower" : string - - a.follow(); ->a.follow() : void ->a.follow : () => void ->a : FollowerGuard ->follow : () => void -} +// if (((a.isLeader)())) { +// a.lead(); +// } +// else if (((a).isFollower())) { +// a.follow(); +// } + +// if (((a["isLeader"])())) { +// a.lead(); +// } +// else if (((a)["isFollower"]())) { +// a.follow(); +// } var holder2 = {a}; >holder2 : { a: RoyalGuard; } From 560e768a5b50c99b3dab2a322f929c51f09c3154 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 9 Apr 2016 18:51:22 -0700 Subject: [PATCH 32/67] Fix linting errors --- src/compiler/binder.ts | 2 -- src/compiler/checker.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 39dca4070a7b3..166064d31bf48 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -441,8 +441,6 @@ namespace ts { let savedCurrentFlow: FlowNode; let savedBreakTarget: FlowLabel; let savedContinueTarget: FlowLabel; - let savedTrueTarget: FlowLabel; - let savedFalseTarget: FlowLabel; let savedActiveLabels: ActiveLabel[]; const kind = node.kind; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e12f65b72911c..997d2fc235243 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7590,7 +7590,7 @@ namespace ts { assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : expr.right.kind === SyntaxKind.NullKeyword ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : - assumeTrue ? TypeFacts.EQUndefined: TypeFacts.NEUndefined; + assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return getTypeWithFacts(type, facts); } From 4c250d046f1c2455862a78a3722dfcf7fbbe9e9a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 10 Apr 2016 09:31:02 -0700 Subject: [PATCH 33/67] Accepting new baselines --- .../reference/equalityWithUnionTypes01.types | 16 ++++----- .../reference/typeAssertions.errors.txt | 34 ++++--------------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/tests/baselines/reference/equalityWithUnionTypes01.types b/tests/baselines/reference/equalityWithUnionTypes01.types index 27bf01c048bf4..61e3803094903 100644 --- a/tests/baselines/reference/equalityWithUnionTypes01.types +++ b/tests/baselines/reference/equalityWithUnionTypes01.types @@ -35,36 +35,36 @@ var z: I1 = x; if (y === z || z === y) { >y === z || z === y : boolean >y === z : boolean ->y : number | I2 +>y : I2 >z : I1 >z === y : boolean >z : I1 ->y : number | I2 +>y : I2 } else if (y !== z || z !== y) { >y !== z || z !== y : boolean >y !== z : boolean ->y : number | I2 +>y : I2 >z : I1 >z !== y : boolean >z : I1 ->y : number | I2 +>y : I2 } else if (y == z || z == y) { >y == z || z == y : boolean >y == z : boolean ->y : number | I2 +>y : I2 >z : I1 >z == y : boolean >z : I1 ->y : number | I2 +>y : I2 } else if (y != z || z != y) { >y != z || z != y : boolean >y != z : boolean ->y : number | I2 +>y : I2 >z : I1 >z != y : boolean >z : I1 ->y : number | I2 +>y : I2 } diff --git a/tests/baselines/reference/typeAssertions.errors.txt b/tests/baselines/reference/typeAssertions.errors.txt index 523d22eb956ba..0468eb23d225c 100644 --- a/tests/baselines/reference/typeAssertions.errors.txt +++ b/tests/baselines/reference/typeAssertions.errors.txt @@ -1,24 +1,14 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(5,5): error TS2346: Supplied parameters do not match any signature of call target. -<<<<<<< HEAD -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. - Property 'p' is missing in type 'SomeOther'. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. -======= +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. Property 'p' is missing in type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. ->>>>>>> master +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. Property 'x' is missing in type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(37,13): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'. Property 'q' is missing in type 'SomeDerived'. -<<<<<<< HEAD -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. -======= tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. ->>>>>>> master +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. Property 'q' is missing in type 'SomeBase'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,5): error TS2304: Cannot find name 'numOrStr'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,14): error TS1005: '>' expected. @@ -71,26 +61,18 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err someBase = someBase; someBase = someOther; // Error ~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD -!!! error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. +!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. ~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Neither type 'SomeOther' nor type 'SomeBase' is assignable to the other. -======= !!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. ->>>>>>> master !!! error TS2352: Property 'p' is missing in type 'SomeOther'. someDerived = someDerived; someDerived = someBase; someDerived = someOther; // Error ~~~~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD -!!! error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. +!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. ~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Neither type 'SomeOther' nor type 'SomeDerived' is assignable to the other. -======= !!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. ->>>>>>> master !!! error TS2352: Property 'x' is missing in type 'SomeOther'. someOther = someDerived; // Error @@ -99,13 +81,9 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err !!! error TS2352: Property 'q' is missing in type 'SomeDerived'. someOther = someBase; // Error ~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD -!!! error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. +!!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. ~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. -======= !!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. ->>>>>>> master !!! error TS2352: Property 'q' is missing in type 'SomeBase'. someOther = someOther; From 7c7a1c000c14651894ce8c2fdec2198a5fef7064 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 12 Apr 2016 09:36:53 -0700 Subject: [PATCH 34/67] A few cosmetic changes --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9233765aeb295..6f506ab0bfda2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -207,7 +207,7 @@ namespace ts { TypeofEQSymbol = 1 << 3, // typeof x === "symbol" TypeofEQObject = 1 << 4, // typeof x === "object" TypeofEQFunction = 1 << 5, // typeof x === "function" - TypeofEQHostObject = 1 << 6, // typeof x === "xxx" + TypeofEQHostObject = 1 << 6, // typeof x === "xxx" TypeofNEString = 1 << 7, // typeof x !== "string" TypeofNENumber = 1 << 8, // typeof x !== "number" TypeofNEBoolean = 1 << 9, // typeof x !== "boolean" @@ -16328,7 +16328,7 @@ namespace ts { } if (entityName.parent.kind === SyntaxKind.ExportAssignment) { - return resolveEntityName(entityName, + return resolveEntityName(entityName, /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } From 586ac55fb4cb0f3cc6812e73ee261e2bc72dd9f1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 12 Apr 2016 13:39:54 -0700 Subject: [PATCH 35/67] Fix finishFlow function and rename to finishFlowLabel --- src/compiler/binder.ts | 55 ++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d72806e2693ed..35c94a64f5026 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -676,16 +676,13 @@ namespace ts { }; } - function finishFlow(flow: FlowNode): FlowNode { - while (flow.kind === FlowKind.Label) { - const antecedents = (flow).antecedents; - if (!antecedents) { - return unreachableFlow; - } - if (antecedents.length > 1) { - break; - } - flow = antecedents[0]; + function finishFlowLabel(flow: FlowLabel): FlowNode { + const antecedents = flow.antecedents; + if (!antecedents) { + return unreachableFlow; + } + if (antecedents.length === 1) { + return antecedents[0]; } return flow; } @@ -760,10 +757,10 @@ namespace ts { addAntecedent(preWhileLabel, currentFlow); currentFlow = preWhileLabel; bindCondition(node.expression, preBodyLabel, postWhileLabel); - currentFlow = finishFlow(preBodyLabel); + currentFlow = finishFlowLabel(preBodyLabel); bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel); addAntecedent(preWhileLabel, currentFlow); - currentFlow = finishFlow(postWhileLabel); + currentFlow = finishFlowLabel(postWhileLabel); } function bindDoStatement(node: DoStatement): void { @@ -774,9 +771,9 @@ namespace ts { currentFlow = preDoLabel; bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); addAntecedent(preConditionLabel, currentFlow); - currentFlow = finishFlow(preConditionLabel); + currentFlow = finishFlowLabel(preConditionLabel); bindCondition(node.expression, preDoLabel, postDoLabel); - currentFlow = finishFlow(postDoLabel); + currentFlow = finishFlowLabel(postDoLabel); } function bindForStatement(node: ForStatement): void { @@ -787,11 +784,11 @@ namespace ts { addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; bindCondition(node.condition, preBodyLabel, postLoopLabel); - currentFlow = finishFlow(preBodyLabel); + currentFlow = finishFlowLabel(preBodyLabel); bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); bind(node.incrementor); addAntecedent(preLoopLabel, currentFlow); - currentFlow = finishFlow(postLoopLabel); + currentFlow = finishFlowLabel(postLoopLabel); } function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { @@ -807,7 +804,7 @@ namespace ts { } bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); addAntecedent(preLoopLabel, currentFlow); - currentFlow = finishFlow(postLoopLabel); + currentFlow = finishFlowLabel(postLoopLabel); } function bindIfStatement(node: IfStatement): void { @@ -815,13 +812,13 @@ namespace ts { const elseLabel = createFlowLabel(); const postIfLabel = createFlowLabel(); bindCondition(node.expression, thenLabel, elseLabel); - currentFlow = finishFlow(thenLabel); + currentFlow = finishFlowLabel(thenLabel); bind(node.thenStatement); addAntecedent(postIfLabel, currentFlow); - currentFlow = finishFlow(elseLabel); + currentFlow = finishFlowLabel(elseLabel); bind(node.elseStatement); addAntecedent(postIfLabel, currentFlow); - currentFlow = finishFlow(postIfLabel); + currentFlow = finishFlowLabel(postIfLabel); } function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void { @@ -880,7 +877,7 @@ namespace ts { currentFlow = preTryFlow; bind(node.finallyBlock); } - currentFlow = finishFlow(postFinallyLabel); + currentFlow = finishFlowLabel(postFinallyLabel); } function bindSwitchStatement(node: SwitchStatement): void { @@ -898,7 +895,7 @@ namespace ts { } currentBreakTarget = saveBreakTarget; preSwitchCaseFlow = savePreSwitchCaseFlow; - currentFlow = finishFlow(postSwitchLabel); + currentFlow = finishFlowLabel(postSwitchLabel); } function bindCaseBlock(node: CaseBlock): void { @@ -913,7 +910,7 @@ namespace ts { const preCaseLabel = createFlowLabel(); addAntecedent(preCaseLabel, preSwitchCaseFlow); addAntecedent(preCaseLabel, currentFlow); - currentFlow = finishFlow(preCaseLabel); + currentFlow = finishFlowLabel(preCaseLabel); } bind(clause); if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { @@ -953,7 +950,7 @@ namespace ts { file.bindDiagnostics.push(createDiagnosticForNode(node.label, Diagnostics.Unused_label)); } addAntecedent(postStatementLabel, currentFlow); - currentFlow = finishFlow(postStatementLabel); + currentFlow = finishFlowLabel(postStatementLabel); } function bindDestructuringTargetFlow(node: Expression) { @@ -999,7 +996,7 @@ namespace ts { else { bindCondition(node.left, trueTarget, preRightLabel); } - currentFlow = finishFlow(preRightLabel); + currentFlow = finishFlowLabel(preRightLabel); bind(node.operatorToken); bindCondition(node.right, trueTarget, falseTarget); } @@ -1024,7 +1021,7 @@ namespace ts { if (isTopLevelLogicalExpression(node)) { const postExpressionLabel = createFlowLabel(); bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); - currentFlow = finishFlow(postExpressionLabel); + currentFlow = finishFlowLabel(postExpressionLabel); } else { bindLogicalExpression(node, currentTrueTarget, currentFalseTarget); @@ -1043,13 +1040,13 @@ namespace ts { const falseLabel = createFlowLabel(); const postExpressionLabel = createFlowLabel(); bindCondition(node.condition, trueLabel, falseLabel); - currentFlow = finishFlow(trueLabel); + currentFlow = finishFlowLabel(trueLabel); bind(node.whenTrue); addAntecedent(postExpressionLabel, currentFlow); - currentFlow = finishFlow(falseLabel); + currentFlow = finishFlowLabel(falseLabel); bind(node.whenFalse); addAntecedent(postExpressionLabel, currentFlow); - currentFlow = finishFlow(postExpressionLabel); + currentFlow = finishFlowLabel(postExpressionLabel); } function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) { From cd88f1ea323f3aa6dbe0ffe3bd0c153a01a46191 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 12 Apr 2016 13:40:21 -0700 Subject: [PATCH 36/67] Adding regression test --- .../cases/conformance/controlFlow/controlFlowWhileStatement.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts index 697a867b88689..dd31114adf53b 100644 --- a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts @@ -37,7 +37,9 @@ function e() { let x: string | number; x = ""; while (cond) { + x; // string | number x = 42; + x; // number } x; // string | number } From 472ab7c8aa702683901eab73b9767d5db0e25167 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 12 Apr 2016 13:40:28 -0700 Subject: [PATCH 37/67] Accepting new baselines --- .../reference/controlFlowWhileStatement.js | 4 +++ .../controlFlowWhileStatement.symbols | 34 +++++++++++-------- .../reference/controlFlowWhileStatement.types | 6 ++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/tests/baselines/reference/controlFlowWhileStatement.js b/tests/baselines/reference/controlFlowWhileStatement.js index fc975764ce195..2dfb716f7cc3c 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.js +++ b/tests/baselines/reference/controlFlowWhileStatement.js @@ -38,7 +38,9 @@ function e() { let x: string | number; x = ""; while (cond) { + x; // string | number x = 42; + x; // number } x; // string | number } @@ -117,7 +119,9 @@ function e() { var x; x = ""; while (cond) { + x; // string | number x = 42; + x; // number } x; // string | number } diff --git a/tests/baselines/reference/controlFlowWhileStatement.symbols b/tests/baselines/reference/controlFlowWhileStatement.symbols index 27e1b533d145c..01af3654f3c37 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.symbols +++ b/tests/baselines/reference/controlFlowWhileStatement.symbols @@ -98,22 +98,28 @@ function e() { while (cond) { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) + x = 42; >x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) + + x; // number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) } x; // string | number >x : Symbol(x, Decl(controlFlowWhileStatement.ts, 36, 7)) } function f() { ->f : Symbol(f, Decl(controlFlowWhileStatement.ts, 42, 1)) +>f : Symbol(f, Decl(controlFlowWhileStatement.ts, 44, 1)) let x: string | number | boolean | RegExp | Function; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) >RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) x = ""; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) while (cond) { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) @@ -122,7 +128,7 @@ function f() { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) x = 42; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) break; } @@ -130,33 +136,33 @@ function f() { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) x = true; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) continue; } x = /a/; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) } x; // string | number | boolean | RegExp ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 44, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 46, 7)) } function g() { ->g : Symbol(g, Decl(controlFlowWhileStatement.ts, 58, 1)) +>g : Symbol(g, Decl(controlFlowWhileStatement.ts, 60, 1)) let x: string | number | boolean | RegExp | Function; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) >RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) x = ""; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) while (true) { if (cond) { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) x = 42; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) break; } @@ -164,14 +170,14 @@ function g() { >cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) x = true; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) continue; } x = /a/; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) } x; // number ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 60, 7)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) } diff --git a/tests/baselines/reference/controlFlowWhileStatement.types b/tests/baselines/reference/controlFlowWhileStatement.types index 7c99b1520486e..3658a20897a83 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.types +++ b/tests/baselines/reference/controlFlowWhileStatement.types @@ -117,10 +117,16 @@ function e() { while (cond) { >cond : boolean + x; // string | number +>x : string | number + x = 42; >x = 42 : number >x : string | number >42 : number + + x; // number +>x : number } x; // string | number >x : string | number From b689c07820b13b0b47026c7e0d8813398f837bda Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 13 Apr 2016 06:48:55 -0700 Subject: [PATCH 38/67] Improving error reporting as suggested in code review --- src/compiler/checker.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369be5258bae7..ac8c375dc9966 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7528,7 +7528,7 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; - return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : initialType; + return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { @@ -7548,6 +7548,10 @@ namespace ts { continue; } return getTypeAtFlowLabel(flow); + case FlowKind.Unreachable: + // Unreachable code errors are reported in the binding phase. Here we + // simply return the declared type to reduce follow-on errors. + return declaredType; } // At the top of the flow we have the initial type return initialType; @@ -7909,6 +7913,8 @@ namespace ts { const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); + // Return the declared type to reduce follow-on errors + return type; } return flowType; } From 1ed987152fc46236cbf71112e6cf244e3e8d190d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 13 Apr 2016 09:30:16 -0700 Subject: [PATCH 39/67] Fix typo --- src/compiler/binder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 35c94a64f5026..6eefd7c6ef9fc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -130,7 +130,7 @@ namespace ts { let classifiableNames: Map; const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; - const reportedUncreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + const reportedUnreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; @@ -2067,7 +2067,7 @@ namespace ts { (node.kind === SyntaxKind.EnumDeclaration && (!isConstEnumDeclaration(node) || options.preserveConstEnums)); if (reportError) { - currentFlow = reportedUncreachableFlow; + currentFlow = reportedUnreachableFlow; // unreachable code is reported if // - user has explicitly asked about it AND From 921efec6b8a30629352d7e36c5251f09087a8888 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 16 Apr 2016 15:11:34 -0700 Subject: [PATCH 40/67] Improved handing of evolving types in iteration statements --- src/compiler/checker.ts | 58 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ac8c375dc9966..1eeec55db8dce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -180,6 +180,7 @@ namespace ts { let jsxElementClassType: Type; let deferredNodes: Node[]; + let inFlowCheck = false; const tupleTypes: Map = {}; const unionTypes: Map = {}; @@ -7528,7 +7529,11 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; - return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; + const saveInFlowCheck = inFlowCheck; + inFlowCheck = true; + const result = reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; + inFlowCheck = saveInFlowCheck; + return result; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { @@ -8537,7 +8542,7 @@ namespace ts { const args = getEffectiveCallArguments(callTarget); const argIndex = indexOf(args, arg); if (argIndex >= 0) { - const signature = getResolvedSignature(callTarget); + const signature = getResolvedOrAnySignature(callTarget); return getTypeAtPosition(signature, argIndex); } return undefined; @@ -11071,6 +11076,20 @@ namespace ts { return resolveCall(node, callSignatures, candidatesOutArray, headMessage); } + function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { + switch (node.kind) { + case SyntaxKind.CallExpression: + return resolveCallExpression(node, candidatesOutArray); + case SyntaxKind.NewExpression: + return resolveNewExpression(node, candidatesOutArray); + case SyntaxKind.TaggedTemplateExpression: + return resolveTaggedTemplateExpression(node, candidatesOutArray); + case SyntaxKind.Decorator: + return resolveDecorator(node, candidatesOutArray); + } + Debug.fail("Branch in 'resolveSignature' should be unreachable."); + } + // candidatesOutArray is passed by signature help in the language service, and collectCandidates // must fill it up with the appropriate candidate signatures function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { @@ -11079,26 +11098,23 @@ namespace ts { // However, it is possible that either candidatesOutArray was not passed in the first time, // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work // to correctly fill the candidatesOutArray. - if (!links.resolvedSignature || candidatesOutArray) { - links.resolvedSignature = anySignature; + const cached = links.resolvedSignature; + if (cached && cached !== anySignature && !candidatesOutArray) { + return cached; + } + links.resolvedSignature = anySignature; + const result = resolveSignature(node, candidatesOutArray); + // If signature resolution originated in control flow type analysis (for example to compute the + // assigned type in a flow assignment) we don't cache the result as it may be based on temporary + // types from the control flow analysis. + links.resolvedSignature = inFlowCheck ? cached : result; + return result; + } - if (node.kind === SyntaxKind.CallExpression) { - links.resolvedSignature = resolveCallExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.NewExpression) { - links.resolvedSignature = resolveNewExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.TaggedTemplateExpression) { - links.resolvedSignature = resolveTaggedTemplateExpression(node, candidatesOutArray); - } - else if (node.kind === SyntaxKind.Decorator) { - links.resolvedSignature = resolveDecorator(node, candidatesOutArray); - } - else { - Debug.fail("Branch in 'getResolvedSignature' should be unreachable."); - } - } - return links.resolvedSignature; + function getResolvedOrAnySignature(node: CallLikeExpression) { + // If we're already in the process of resolving the given signature, don't resolve again as + // that could cause infinite recursion. Instead, return anySignature. + return getNodeLinks(node).resolvedSignature === anySignature ? anySignature : getResolvedSignature(node); } function getInferredClassType(symbol: Symbol) { From 9aea70895346bf70022037c6d3c9856c862d2387 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 16 Apr 2016 15:13:31 -0700 Subject: [PATCH 41/67] Adding tests --- .../controlFlow/controlFlowIterationErrors.ts | 48 +++++++++++++++++++ .../controlFlow/controlFlowWhileStatement.ts | 29 +++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts diff --git a/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts b/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts new file mode 100644 index 0000000000000..a8a580150e8dc --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts @@ -0,0 +1,48 @@ +let cond: boolean; + +function len(s: string) { + return s.length; +} + +function f1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = len(x); + x; + } + x; +} + +function f2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = len(x); + } + x; +} + +declare function foo(x: string): number; +declare function foo(x: number): string; + +function g1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = foo(x); + x; + } + x; +} + +function g2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = foo(x); + } + x; +} diff --git a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts index dd31114adf53b..7bf49dd722429 100644 --- a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts @@ -75,3 +75,32 @@ function g() { } x; // number } +function h1() { + let x: string | number | boolean; + x = ""; + while (x > 1) { + x; // string | number + x = 1; + x; // number + } + x; // string | number +} +declare function len(s: string | number): number; +function h2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = len(x); + x; // number + } + x; // string | number +} +function h3() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; // string | number + x = len(x); + } + x; // string | number +} From 10889a042c24ad8ce017450cb076404c0076ee0d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 16 Apr 2016 15:13:48 -0700 Subject: [PATCH 42/67] Accepting new baselines --- .../controlFlowIterationErrors.errors.txt | 72 +++++++++++++++ .../reference/controlFlowIterationErrors.js | 92 +++++++++++++++++++ .../reference/controlFlowWhileStatement.js | 57 ++++++++++++ .../controlFlowWhileStatement.symbols | 74 +++++++++++++++ .../reference/controlFlowWhileStatement.types | 88 ++++++++++++++++++ 5 files changed, 383 insertions(+) create mode 100644 tests/baselines/reference/controlFlowIterationErrors.errors.txt create mode 100644 tests/baselines/reference/controlFlowIterationErrors.js diff --git a/tests/baselines/reference/controlFlowIterationErrors.errors.txt b/tests/baselines/reference/controlFlowIterationErrors.errors.txt new file mode 100644 index 0000000000000..748bf05dfb74b --- /dev/null +++ b/tests/baselines/reference/controlFlowIterationErrors.errors.txt @@ -0,0 +1,72 @@ +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(11,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(22,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(34,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. + Type 'string' is not assignable to type 'number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(45,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (4 errors) ==== + let cond: boolean; + + function len(s: string) { + return s.length; + } + + function f1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = len(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2345: Type 'number' is not assignable to type 'string'. + x; + } + x; + } + + function f2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = len(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2345: Type 'number' is not assignable to type 'string'. + } + x; + } + + declare function foo(x: string): number; + declare function foo(x: number): string; + + function g1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = foo(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. + x; + } + x; + } + + function g2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = foo(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. + } + x; + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowIterationErrors.js b/tests/baselines/reference/controlFlowIterationErrors.js new file mode 100644 index 0000000000000..6c4a9a0b7e042 --- /dev/null +++ b/tests/baselines/reference/controlFlowIterationErrors.js @@ -0,0 +1,92 @@ +//// [controlFlowIterationErrors.ts] +let cond: boolean; + +function len(s: string) { + return s.length; +} + +function f1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = len(x); + x; + } + x; +} + +function f2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = len(x); + } + x; +} + +declare function foo(x: string): number; +declare function foo(x: number): string; + +function g1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = foo(x); + x; + } + x; +} + +function g2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = foo(x); + } + x; +} + + +//// [controlFlowIterationErrors.js] +var cond; +function len(s) { + return s.length; +} +function f1() { + var x; + x = ""; + while (cond) { + x = len(x); + x; + } + x; +} +function f2() { + var x; + x = ""; + while (cond) { + x; + x = len(x); + } + x; +} +function g1() { + var x; + x = ""; + while (cond) { + x = foo(x); + x; + } + x; +} +function g2() { + var x; + x = ""; + while (cond) { + x; + x = foo(x); + } + x; +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.js b/tests/baselines/reference/controlFlowWhileStatement.js index 2dfb716f7cc3c..d9faf9bd0eae7 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.js +++ b/tests/baselines/reference/controlFlowWhileStatement.js @@ -76,6 +76,35 @@ function g() { } x; // number } +function h1() { + let x: string | number | boolean; + x = ""; + while (x > 1) { + x; // string | number + x = 1; + x; // number + } + x; // string | number +} +declare function len(s: string | number): number; +function h2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = len(x); + x; // number + } + x; // string | number +} +function h3() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; // string | number + x = len(x); + } + x; // string | number +} //// [controlFlowWhileStatement.js] @@ -157,3 +186,31 @@ function g() { } x; // number } +function h1() { + var x; + x = ""; + while (x > 1) { + x; // string | number + x = 1; + x; // number + } + x; // string | number +} +function h2() { + var x; + x = ""; + while (cond) { + x = len(x); + x; // number + } + x; // string | number +} +function h3() { + var x; + x = ""; + while (cond) { + x; // string | number + x = len(x); + } + x; // string | number +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.symbols b/tests/baselines/reference/controlFlowWhileStatement.symbols index 01af3654f3c37..3b4def93b4513 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.symbols +++ b/tests/baselines/reference/controlFlowWhileStatement.symbols @@ -180,4 +180,78 @@ function g() { x; // number >x : Symbol(x, Decl(controlFlowWhileStatement.ts, 62, 7)) } +function h1() { +>h1 : Symbol(h1, Decl(controlFlowWhileStatement.ts, 76, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + + while (x > 1) { +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + + x = 1; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + + x; // number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) + } + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 78, 7)) +} +declare function len(s: string | number): number; +>len : Symbol(len, Decl(controlFlowWhileStatement.ts, 86, 1)) +>s : Symbol(s, Decl(controlFlowWhileStatement.ts, 87, 21)) + +function h2() { +>h2 : Symbol(h2, Decl(controlFlowWhileStatement.ts, 87, 49)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = len(x); +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) +>len : Symbol(len, Decl(controlFlowWhileStatement.ts, 86, 1)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) + + x; // number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) + } + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 89, 7)) +} +function h3() { +>h3 : Symbol(h3, Decl(controlFlowWhileStatement.ts, 96, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) + + x = len(x); +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) +>len : Symbol(len, Decl(controlFlowWhileStatement.ts, 86, 1)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) + } + x; // string | number +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.types b/tests/baselines/reference/controlFlowWhileStatement.types index 3658a20897a83..a17c084228bd3 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.types +++ b/tests/baselines/reference/controlFlowWhileStatement.types @@ -219,4 +219,92 @@ function g() { x; // number >x : number } +function h1() { +>h1 : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + + while (x > 1) { +>x > 1 : boolean +>x : string | number +>1 : number + + x; // string | number +>x : string | number + + x = 1; +>x = 1 : number +>x : string | number | boolean +>1 : number + + x; // number +>x : number + } + x; // string | number +>x : string | number +} +declare function len(s: string | number): number; +>len : (s: string | number) => number +>s : string | number + +function h2() { +>h2 : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + + while (cond) { +>cond : boolean + + x = len(x); +>x = len(x) : number +>x : string | number | boolean +>len(x) : number +>len : (s: string | number) => number +>x : string | number + + x; // number +>x : number + } + x; // string | number +>x : string | number +} +function h3() { +>h3 : () => void + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : string +>x : string | number | boolean +>"" : string + + while (cond) { +>cond : boolean + + x; // string | number +>x : string | number + + x = len(x); +>x = len(x) : number +>x : string | number | boolean +>len(x) : number +>len : (s: string | number) => number +>x : string | number + } + x; // string | number +>x : string | number +} From 2595f0451c2930224f3cc51bca39935e77f449c5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 17 Apr 2016 13:48:41 -0700 Subject: [PATCH 43/67] Removing unused properties --- src/compiler/types.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a89f7dcca8e06..b5e33d5d361a4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2083,8 +2083,6 @@ namespace ts { isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration bindingElement?: BindingElement; // Binding element associated with property symbol exportsSomeValue?: boolean; // True if module exports some value (not just types) - firstAssignmentChecked?: boolean; // True if first assignment node has been computed - firstAssignment?: Node; // First assignment node (undefined if no assignments) } /* @internal */ @@ -2118,18 +2116,13 @@ namespace ts { /* @internal */ export interface NodeLinks { resolvedType?: Type; // Cached type of type node - resolvedAwaitedType?: Type; // Cached awaited type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result flags?: NodeCheckFlags; // Set of flags specific to Node enumMemberValue?: number; // Constant value of enum member isVisible?: boolean; // Is this node visible - generatedName?: string; // Generated name for module, enum, or import declaration - generatedNames?: Map; // Generated names table for source file - assignmentMap?: Map; // Cached map of references assigned within this node hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context - importOnRightSide?: Symbol; // for import declarations - import that appear on the right side jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. From b83dc88f9b9f0d874adea078a8c5efceb6841ab0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 13:51:40 -0700 Subject: [PATCH 44/67] Improve expression type caching to ensure consistent results --- src/compiler/checker.ts | 71 ++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1eeec55db8dce..a7a875892d865 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -180,7 +180,9 @@ namespace ts { let jsxElementClassType: Type; let deferredNodes: Node[]; - let inFlowCheck = false; + + let flowStackStart = 0; + let flowStackCount = 0; const tupleTypes: Map = {}; const unionTypes: Map = {}; @@ -195,6 +197,8 @@ namespace ts { const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; const flowTypeCaches: Map[] = []; + const flowStackNodes: FlowNode[] = []; + const flowStackCacheKeys: string[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -7409,7 +7413,7 @@ namespace ts { function getTypeWithDefault(type: Type, defaultExpression: Expression) { if (defaultExpression) { - const defaultType = checkExpressionCached(defaultExpression); + const defaultType = checkExpression(defaultExpression); return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]); } return type; @@ -7436,7 +7440,7 @@ namespace ts { function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ? getTypeWithDefault(getAssignedType(node), node.right) : - checkExpressionCached(node.right); + checkExpression(node.right); } function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { @@ -7487,9 +7491,17 @@ namespace ts { return getTypeWithDefault(type, node.initializer); } + function getTypeOfInitializer(node: Expression) { + // Return the cached type if one is available. If the type of the variable was inferred + // from its initializer, we'll already have cached the type. Otherwise we compute it now + // without caching such that transient types are reflected. + const links = getNodeLinks(node); + return links.resolvedType || checkExpression(node); + } + function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { if (node.initializer) { - return checkExpressionCached(node.initializer); + return getTypeOfInitializer(node.initializer); } if (node.parent.parent.kind === SyntaxKind.ForInStatement) { return stringType; @@ -7529,11 +7541,7 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; - const saveInFlowCheck = inFlowCheck; - inFlowCheck = true; - const result = reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; - inFlowCheck = saveInFlowCheck; - return result; + return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { @@ -7601,30 +7609,41 @@ namespace ts { if (!key) { key = getFlowCacheKey(reference); } - let type = cache[key]; - if (type) { - return type; + const cached = cache[key]; + if (cached) { + return cached; } - cache[key] = resolvingFlowType; - type = getTypeAtFlowNode(flow); - cache[key] = type !== resolvingFlowType ? type : undefined; - return type; + // Return undefined if we're already processing the given node. + for (let i = flowStackStart; i < flowStackCount; i++) { + if (flowStackNodes[i] === flow && flowStackCacheKeys[i] === key) { + return undefined; + } + } + // Record node and key on stack of nodes being processed. + flowStackNodes[flowStackCount] = flow; + flowStackCacheKeys[flowStackCount] = key; + flowStackCount++; + const type = getTypeAtFlowNode(flow); + flowStackCount--; + // Record the result only if the cache is still empty. If checkExpressionCached was called + // during processing it is possible we've already recorded a result. + return cache[key] || (cache[key] = type); } function getTypeAtFlowLabel(flow: FlowLabel) { const antecedentTypes: Type[] = []; for (const antecedent of flow.antecedents) { - const t = getTypeAtFlowNodeCached(antecedent); - if (t !== resolvingFlowType) { + const type = getTypeAtFlowNodeCached(antecedent); + if (type) { // If the type at a particular antecedent path is the declared type and the // reference is known to always be assigned (i.e. when declared and initial types // are the same), there is no reason to process more antecedents since the only // possible outcome is subtypes that will be removed in the final union type anyway. - if (t === declaredType && declaredType === initialType) { - return t; + if (type === declaredType && declaredType === initialType) { + return type; } - if (!contains(antecedentTypes, t)) { - antecedentTypes.push(t); + if (!contains(antecedentTypes, type)) { + antecedentTypes.push(type); } } } @@ -11107,7 +11126,7 @@ namespace ts { // If signature resolution originated in control flow type analysis (for example to compute the // assigned type in a flow assignment) we don't cache the result as it may be based on temporary // types from the control flow analysis. - links.resolvedSignature = inFlowCheck ? cached : result; + links.resolvedSignature = flowStackStart === flowStackCount ? result : cached; return result; } @@ -12246,7 +12265,13 @@ namespace ts { function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { const links = getNodeLinks(node); if (!links.resolvedType) { + // When computing a type that we're going to cache, we need to ignore any ongoing control flow + // analysis because variables may have transient types in indeterminable states. Moving flowStackStart + // to the top of the stack ensures all transient types are computed from a known point. + const saveFlowStackStart = flowStackStart; + flowStackStart = flowStackCount; links.resolvedType = checkExpression(node, contextualMapper); + flowStackStart = saveFlowStackStart; } return links.resolvedType; } From 538e22a35eb515ade1fe0a944f305b8f0ae6b101 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 13:51:51 -0700 Subject: [PATCH 45/67] Adding tests --- .../controlFlow/controlFlowIterationErrors.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts b/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts index a8a580150e8dc..2e54b3355377d 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts @@ -1,3 +1,5 @@ +// @noImplicitAny: true + let cond: boolean; function len(s: string) { @@ -46,3 +48,46 @@ function g2() { } x; } + +function asNumber(x: string | number): number { + return +x; +} + +function h1() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = +x + 1; + x; + } +} + +function h2() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = asNumber(x) + 1; + x; + } +} + +function h3() { + let x: string | number | boolean; + x = "0"; + while (cond) { + let y = asNumber(x); + x = y + 1; + x; + } +} + +function h4() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x; + let y = asNumber(x); + x = y + 1; + x; + } +} From b5104cbe69f9ac65114f7ee646487df80bdfedf6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 15:39:49 -0700 Subject: [PATCH 46/67] Accepting new baselines --- .../controlFlowIterationErrors.errors.txt | 70 ++++++++++++++-- .../reference/controlFlowIterationErrors.js | 82 +++++++++++++++++++ 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/tests/baselines/reference/controlFlowIterationErrors.errors.txt b/tests/baselines/reference/controlFlowIterationErrors.errors.txt index 748bf05dfb74b..737a9e9557bfb 100644 --- a/tests/baselines/reference/controlFlowIterationErrors.errors.txt +++ b/tests/baselines/reference/controlFlowIterationErrors.errors.txt @@ -1,14 +1,21 @@ -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(11,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(12,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. Type 'number' is not assignable to type 'string'. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(22,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(23,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. Type 'number' is not assignable to type 'string'. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(34,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(35,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(45,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(46,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(77,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(77,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. + Type 'boolean' is not assignable to type 'string | number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. + Type 'boolean' is not assignable to type 'string | number'. -==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (4 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (8 errors) ==== + let cond: boolean; function len(s: string) { @@ -69,4 +76,57 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(45,17): error } x; } + + function asNumber(x: string | number): number { + return +x; + } + + function h1() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = +x + 1; + x; + } + } + + function h2() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = asNumber(x) + 1; + x; + } + } + + function h3() { + let x: string | number | boolean; + x = "0"; + while (cond) { + let y = asNumber(x); + ~ +!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~ +!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. +!!! error TS2345: Type 'boolean' is not assignable to type 'string | number'. + x = y + 1; + x; + } + } + + function h4() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x; + let y = asNumber(x); + ~ +!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~ +!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. +!!! error TS2345: Type 'boolean' is not assignable to type 'string | number'. + x = y + 1; + x; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowIterationErrors.js b/tests/baselines/reference/controlFlowIterationErrors.js index 6c4a9a0b7e042..eed54cc53a797 100644 --- a/tests/baselines/reference/controlFlowIterationErrors.js +++ b/tests/baselines/reference/controlFlowIterationErrors.js @@ -1,4 +1,5 @@ //// [controlFlowIterationErrors.ts] + let cond: boolean; function len(s: string) { @@ -47,6 +48,49 @@ function g2() { } x; } + +function asNumber(x: string | number): number { + return +x; +} + +function h1() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = +x + 1; + x; + } +} + +function h2() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = asNumber(x) + 1; + x; + } +} + +function h3() { + let x: string | number | boolean; + x = "0"; + while (cond) { + let y = asNumber(x); + x = y + 1; + x; + } +} + +function h4() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x; + let y = asNumber(x); + x = y + 1; + x; + } +} //// [controlFlowIterationErrors.js] @@ -90,3 +134,41 @@ function g2() { } x; } +function asNumber(x) { + return +x; +} +function h1() { + var x; + x = "0"; + while (cond) { + x = +x + 1; + x; + } +} +function h2() { + var x; + x = "0"; + while (cond) { + x = asNumber(x) + 1; + x; + } +} +function h3() { + var x; + x = "0"; + while (cond) { + var y = asNumber(x); + x = y + 1; + x; + } +} +function h4() { + var x; + x = "0"; + while (cond) { + x; + var y = asNumber(x); + x = y + 1; + x; + } +} From 87f55fa68351d39ac4b6d2654c8a079f7168d469 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 16:37:58 -0700 Subject: [PATCH 47/67] Only evaluate assigned type when declared type is a union type --- src/compiler/checker.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a7a875892d865..977bb38ed5b8a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7343,9 +7343,9 @@ namespace ts { // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. - function getAssignmentReducedType(declaredType: Type, assignedType: Type) { + function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) { - const reducedTypes = filter((declaredType).types, t => typeMaybeAssignableTo(assignedType, t)); + const reducedTypes = filter(declaredType.types, t => typeMaybeAssignableTo(assignedType, t)); if (reducedTypes.length) { return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); } @@ -7573,16 +7573,22 @@ namespace ts { function getTypeAtFlowAssignment(flow: FlowAssignment) { 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 && getResolvedSymbol(reference) === getSymbolOfNode(node)) { - return getAssignmentReducedType(declaredType, getInitialType(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 getAssignmentReducedType(declaredType, getAssignedType(node)); + return declaredType.flags & TypeFlags.Union ? + getAssignmentReducedType(declaredType, getAssignedType(node)) : + declaredType; } // We didn't have a direct match. However, if the reference is a dotted name, this // may be an assignment to a left hand part of the reference. For example, for a From 9defdde02f3c02d968db1345f4ad689129779e5e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 16:39:15 -0700 Subject: [PATCH 48/67] Accepting new baselines --- tests/baselines/reference/typeAssertions.errors.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/baselines/reference/typeAssertions.errors.txt b/tests/baselines/reference/typeAssertions.errors.txt index 0468eb23d225c..e330c23d737c3 100644 --- a/tests/baselines/reference/typeAssertions.errors.txt +++ b/tests/baselines/reference/typeAssertions.errors.txt @@ -1,13 +1,10 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(5,5): error TS2346: Supplied parameters do not match any signature of call target. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. Property 'p' is missing in type 'SomeOther'. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. Property 'x' is missing in type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(37,13): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'. Property 'q' is missing in type 'SomeDerived'. -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. Property 'q' is missing in type 'SomeBase'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,5): error TS2304: Cannot find name 'numOrStr'. @@ -26,7 +23,7 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,44): err tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): error TS1005: ';' expected. -==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (21 errors) ==== +==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (18 errors) ==== // Function call whose argument is a 1 arg generic function call with explicit type arguments function fn1(t: T) { } function fn2(t: any) { } @@ -61,8 +58,6 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err someBase = someBase; someBase = someOther; // Error ~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. - ~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. !!! error TS2352: Property 'p' is missing in type 'SomeOther'. @@ -70,8 +65,6 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err someDerived = someBase; someDerived = someOther; // Error ~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. - ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. !!! error TS2352: Property 'x' is missing in type 'SomeOther'. @@ -81,8 +74,6 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err !!! error TS2352: Property 'q' is missing in type 'SomeDerived'. someOther = someBase; // Error ~~~~~~~~~~~~~~~~~~~ -!!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. - ~~~~~~~~~~~~~~~~~~~ !!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. !!! error TS2352: Property 'q' is missing in type 'SomeBase'. someOther = someOther; From d28a4feeba6bc7eba7c924674e0bc64657259f31 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Apr 2016 16:58:22 -0700 Subject: [PATCH 49/67] typeof x === "function" type guards include Function interface --- 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 977bb38ed5b8a..d58ad9fbf9ad2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7366,7 +7366,7 @@ namespace ts { } if (flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - return resolved.callSignatures.length || resolved.constructSignatures.length ? + return resolved.callSignatures.length || resolved.constructSignatures.length || isTypeSubtypeOf(type, globalFunctionType) ? strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } From d735b7acbf790376d380a127f1de7950016b8b42 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Apr 2016 10:04:17 -0700 Subject: [PATCH 50/67] Variables from different source files default to their declared type --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d58ad9fbf9ad2..bab573c8b7f6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7936,7 +7936,7 @@ namespace ts { const declaration = localOrExportSymbol.valueDeclaration; const defaultsToDeclaredType = !strictNullChecks || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - getContainingFunction(declaration) !== getContainingFunction(node); + getContainingFunctionOrSourceFile(declaration) !== getContainingFunctionOrSourceFile(node); if (defaultsToDeclaredType && !(type.flags & TypeFlags.Narrowable)) { return type; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 638c7a60aa1dd..2e51f6a150408 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -840,6 +840,15 @@ namespace ts { } } + export function getContainingFunctionOrSourceFile(node: Node): FunctionLikeDeclaration | SourceFile { + while (true) { + node = node.parent; + if (isFunctionLike(node) || node.kind === SyntaxKind.SourceFile) { + return node; + } + } + } + export function getContainingClass(node: Node): ClassLikeDeclaration { while (true) { node = node.parent; From c8bf6d821a5de7329ded94af1fa743068e85ace3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Apr 2016 10:14:02 -0700 Subject: [PATCH 51/67] Variables from different module declarations default to their declared type --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bab573c8b7f6b..4731046864e95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7936,7 +7936,7 @@ namespace ts { const declaration = localOrExportSymbol.valueDeclaration; const defaultsToDeclaredType = !strictNullChecks || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - getContainingFunctionOrSourceFile(declaration) !== getContainingFunctionOrSourceFile(node); + getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); if (defaultsToDeclaredType && !(type.flags & TypeFlags.Narrowable)) { return type; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2e51f6a150408..60b041d843c88 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -840,11 +840,11 @@ namespace ts { } } - export function getContainingFunctionOrSourceFile(node: Node): FunctionLikeDeclaration | SourceFile { + export function getContainingFunctionOrModule(node: Node): Node { while (true) { node = node.parent; - if (isFunctionLike(node) || node.kind === SyntaxKind.SourceFile) { - return node; + if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) { + return node; } } } From ea96dfd364f78caf397a21416765a2f48ae55434 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 Apr 2016 06:59:28 -0700 Subject: [PATCH 52/67] Support comma operator in type guards --- src/compiler/binder.ts | 2 ++ src/compiler/checker.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6eefd7c6ef9fc..91f4247a67307 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -630,6 +630,8 @@ namespace ts { return false; case SyntaxKind.InstanceOfKeyword: return isNarrowingExpression(expr.left); + case SyntaxKind.CommaToken: + return isNarrowingExpression(expr.right); } return false; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4731046864e95..09d70ac3684ae 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7677,6 +7677,8 @@ namespace ts { break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); + case SyntaxKind.CommaToken: + return narrowType(type, expr.right, assumeTrue); } return type; } From 33e359ff135856b44b26ba23ce9a275e1e6a3b48 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 Apr 2016 07:09:43 -0700 Subject: [PATCH 53/67] Adding test --- .../controlFlow/controlFlowCommaOperator.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts diff --git a/tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts b/tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts new file mode 100644 index 0000000000000..d6d49e48f34ac --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts @@ -0,0 +1,22 @@ +function f(x: string | number | boolean) { + let y: string | number | boolean = false; + let z: string | number | boolean = false; + if (y = "", typeof x === "string") { + x; // string + y; // string + z; // boolean + } + else if (z = 1, typeof x === "number") { + x; // number + y; // string + z; // number + } + else { + x; // boolean + y; // string + z; // number + } + x; // string | number | boolean + y; // string + z; // number | boolean +} From bab8ef4b10046e7ef2f1d7ed6ebf22b6b07f7d14 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 Apr 2016 07:09:53 -0700 Subject: [PATCH 54/67] Accepting new baselines --- .../reference/controlFlowCommaOperator.js | 48 +++++++++++++ .../controlFlowCommaOperator.symbols | 57 +++++++++++++++ .../reference/controlFlowCommaOperator.types | 71 +++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 tests/baselines/reference/controlFlowCommaOperator.js create mode 100644 tests/baselines/reference/controlFlowCommaOperator.symbols create mode 100644 tests/baselines/reference/controlFlowCommaOperator.types diff --git a/tests/baselines/reference/controlFlowCommaOperator.js b/tests/baselines/reference/controlFlowCommaOperator.js new file mode 100644 index 0000000000000..55dcc77e07586 --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaOperator.js @@ -0,0 +1,48 @@ +//// [controlFlowCommaOperator.ts] +function f(x: string | number | boolean) { + let y: string | number | boolean = false; + let z: string | number | boolean = false; + if (y = "", typeof x === "string") { + x; // string + y; // string + z; // boolean + } + else if (z = 1, typeof x === "number") { + x; // number + y; // string + z; // number + } + else { + x; // boolean + y; // string + z; // number + } + x; // string | number | boolean + y; // string + z; // number | boolean +} + + +//// [controlFlowCommaOperator.js] +function f(x) { + var y = false; + var z = false; + if (y = "", typeof x === "string") { + x; // string + y; // string + z; // boolean + } + else if (z = 1, typeof x === "number") { + x; // number + y; // string + z; // number + } + else { + x; // boolean + y; // string + z; // number + } + x; // string | number | boolean + y; // string + z; // number | boolean +} diff --git a/tests/baselines/reference/controlFlowCommaOperator.symbols b/tests/baselines/reference/controlFlowCommaOperator.symbols new file mode 100644 index 0000000000000..f1550b79a681f --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaOperator.symbols @@ -0,0 +1,57 @@ +=== tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts === +function f(x: string | number | boolean) { +>f : Symbol(f, Decl(controlFlowCommaOperator.ts, 0, 0)) +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + let y: string | number | boolean = false; +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) + + let z: string | number | boolean = false; +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) + + if (y = "", typeof x === "string") { +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + x; // string +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + y; // string +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) + + z; // boolean +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) + } + else if (z = 1, typeof x === "number") { +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + x; // number +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + y; // string +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) + + z; // number +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) + } + else { + x; // boolean +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + y; // string +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) + + z; // number +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) + } + x; // string | number | boolean +>x : Symbol(x, Decl(controlFlowCommaOperator.ts, 0, 11)) + + y; // string +>y : Symbol(y, Decl(controlFlowCommaOperator.ts, 1, 7)) + + z; // number | boolean +>z : Symbol(z, Decl(controlFlowCommaOperator.ts, 2, 7)) +} + diff --git a/tests/baselines/reference/controlFlowCommaOperator.types b/tests/baselines/reference/controlFlowCommaOperator.types new file mode 100644 index 0000000000000..53014ceafa7b0 --- /dev/null +++ b/tests/baselines/reference/controlFlowCommaOperator.types @@ -0,0 +1,71 @@ +=== tests/cases/conformance/controlFlow/controlFlowCommaOperator.ts === +function f(x: string | number | boolean) { +>f : (x: string | number | boolean) => void +>x : string | number | boolean + + let y: string | number | boolean = false; +>y : string | number | boolean +>false : boolean + + let z: string | number | boolean = false; +>z : string | number | boolean +>false : boolean + + if (y = "", typeof x === "string") { +>y = "", typeof x === "string" : boolean +>y = "" : string +>y : string | number | boolean +>"" : string +>typeof x === "string" : boolean +>typeof x : string +>x : string | number | boolean +>"string" : string + + x; // string +>x : string + + y; // string +>y : string + + z; // boolean +>z : boolean + } + else if (z = 1, typeof x === "number") { +>z = 1, typeof x === "number" : boolean +>z = 1 : number +>z : string | number | boolean +>1 : number +>typeof x === "number" : boolean +>typeof x : string +>x : number | boolean +>"number" : string + + x; // number +>x : number + + y; // string +>y : string + + z; // number +>z : number + } + else { + x; // boolean +>x : boolean + + y; // string +>y : string + + z; // number +>z : number + } + x; // string | number | boolean +>x : string | number | boolean + + y; // string +>y : string + + z; // number | boolean +>z : boolean | number +} + From e9a7d3d98c9e657b2b159555ded3665fd750ba9b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 Apr 2016 09:15:57 -0700 Subject: [PATCH 55/67] Removing unused logic --- src/compiler/checker.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 09d70ac3684ae..aa0f9fe2af870 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -119,7 +119,6 @@ namespace ts { const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null"); const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined"); const unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); - const resolvingFlowType = createIntrinsicType(TypeFlags.Void, "__resolving__"); const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyUnionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); @@ -7603,11 +7602,7 @@ namespace ts { } function getTypeAtFlowCondition(flow: FlowCondition) { - const type = getTypeAtFlowNode(flow.antecedent); - if (type === resolvingFlowType) { - return type; - } - return narrowType(type, (flow).expression, (flow).assumeTrue); + return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, flow.assumeTrue); } function getTypeAtFlowNodeCached(flow: FlowNode) { From a0101c0787e027725cd3224e71de5177368b556a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 10:48:34 -0700 Subject: [PATCH 56/67] Improve consistency of instanceof and user defined type guards --- src/compiler/checker.ts | 59 ++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aa0f9fe2af870..195245648a7e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7339,6 +7339,18 @@ namespace ts { return false; } + function typeMaybeSubtypeOf(source: Type, target: Type) { + if (!(source.flags & TypeFlags.Union)) { + return isTypeSubtypeOf(source, target); + } + for (const t of (source).types) { + if (isTypeSubtypeOf(t, target)) { + return true; + } + } + return false; + } + // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. @@ -7766,30 +7778,29 @@ namespace ts { return type; } - function getNarrowedType(originalType: Type, narrowedTypeCandidate: Type, assumeTrue: boolean) { - if (!assumeTrue) { - if (originalType.flags & TypeFlags.Union) { - return getUnionType(filter((originalType).types, t => !isTypeSubtypeOf(t, narrowedTypeCandidate))); - } - return originalType; - } - - // If the current type is a union type, remove all constituents that aren't assignable to target. If that produces - // 0 candidates, fall back to the assignability check - if (originalType.flags & TypeFlags.Union) { - const assignableConstituents = filter((originalType).types, t => isTypeAssignableTo(t, narrowedTypeCandidate)); - if (assignableConstituents.length) { - return getUnionType(assignableConstituents); - } - } - - const targetType = originalType.flags & TypeFlags.TypeParameter ? getApparentType(originalType) : originalType; - if (isTypeAssignableTo(narrowedTypeCandidate, targetType)) { - // Narrow to the target type if it's assignable to the current type - return narrowedTypeCandidate; - } - - return originalType; + function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { + if (typeMaybeSubtypeOf(type, candidate)) { + // If the current type, or a constituent of the current type, is a subtype of + // the candidate type then the current type contains the most specific information. + // and we simply filter out constituents that aren't applicable. For example, + // if the current type is string | string[] and the candidate type is any[], + // we filter out string. + return type.flags & TypeFlags.Union ? + getUnionType(filter((type).types, t => isTypeSubtypeOf(t, candidate) === assumeTrue)) : + assumeTrue ? type : emptyUnionType; + } + if (assumeTrue) { + // In the true branch of the remaining cases, if the candidate type is assignable + // to the current type, then we narrow to the candidate type. Otherwise, we narrow + // to an empty type (because we now know the types are completely unrelated). For + // example, if the current type is Object and the candidate type is string[], we + // narrow to string[]. But if the current type is string and the candidate is + // string[], we narrow to the empty type. + const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; + return isTypeAssignableTo(candidate, targetType) ? candidate : emptyUnionType; + } + // In the false branch of the remaining cases we leave the type unchanged. + return type; } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { From 729dfceb457943363bfa883b91639a673ae7d975 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 10:49:19 -0700 Subject: [PATCH 57/67] Fix incorrect user defined type guard function in compiler --- src/compiler/emitter.ts | 2 +- src/compiler/utilities.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 254b8d7e61b17..19c47db2193c9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -7084,7 +7084,7 @@ const _super = (function (geti, seti) { hoistedVars = []; } - hoistedVars.push(node.name); + hoistedVars.push((node).name); return; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 60b041d843c88..65405db79aec2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1178,7 +1178,7 @@ namespace ts { return ((node).moduleReference).expression; } - export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { + export function isInternalModuleImportEqualsDeclaration(node: Node): boolean { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind !== SyntaxKind.ExternalModuleReference; } From 3045cf5fa2a2ea7657b2030e3acebcc69c3779d0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 10:49:39 -0700 Subject: [PATCH 58/67] Add regression test --- .../controlFlowBinaryOrExpression.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts b/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts index 75b24622e0028..9dab32e0cb835 100644 --- a/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts +++ b/tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts @@ -7,3 +7,29 @@ x; // string | number x = ""; cond || (x = 0); x; // string | number + +export interface NodeList { + length: number; +} + +export interface HTMLCollection { + length: number; +} + +declare function isNodeList(sourceObj: any): sourceObj is NodeList; +declare function isHTMLCollection(sourceObj: any): sourceObj is HTMLCollection; + +type EventTargetLike = {a: string} | HTMLCollection | NodeList; + +var sourceObj: EventTargetLike = undefined; +if (isNodeList(sourceObj)) { + sourceObj.length; +} + +if (isHTMLCollection(sourceObj)) { + sourceObj.length; +} + +if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { + sourceObj.length; +} From 06928b669e69495333ef51ed3e02332898cdd1b7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 10:50:08 -0700 Subject: [PATCH 59/67] Accepting new baselines --- .../controlFlowBinaryOrExpression.js | 37 +++++++++ .../controlFlowBinaryOrExpression.symbols | 69 +++++++++++++++++ .../controlFlowBinaryOrExpression.types | 75 +++++++++++++++++++ .../reference/instanceOfAssignability.types | 4 +- .../stringLiteralTypesAsTags01.types | 8 +- .../stringLiteralTypesAsTags02.types | 8 +- .../stringLiteralTypesAsTags03.types | 8 +- .../typeGuardsWithInstanceOf.errors.txt | 14 ++++ .../typeGuardsWithInstanceOf.symbols | 26 ------- .../reference/typeGuardsWithInstanceOf.types | 31 -------- 10 files changed, 209 insertions(+), 71 deletions(-) create mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt delete mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.symbols delete mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.types diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.js b/tests/baselines/reference/controlFlowBinaryOrExpression.js index 350e383a1f8e6..fb48c5a23e031 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.js +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.js @@ -8,9 +8,36 @@ x; // string | number x = ""; cond || (x = 0); x; // string | number + +export interface NodeList { + length: number; +} + +export interface HTMLCollection { + length: number; +} + +declare function isNodeList(sourceObj: any): sourceObj is NodeList; +declare function isHTMLCollection(sourceObj: any): sourceObj is HTMLCollection; + +type EventTargetLike = {a: string} | HTMLCollection | NodeList; + +var sourceObj: EventTargetLike = undefined; +if (isNodeList(sourceObj)) { + sourceObj.length; +} + +if (isHTMLCollection(sourceObj)) { + sourceObj.length; +} + +if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { + sourceObj.length; +} //// [controlFlowBinaryOrExpression.js] +"use strict"; var x; var cond; (x = "") || (x = 0); @@ -18,3 +45,13 @@ x; // string | number x = ""; cond || (x = 0); x; // string | number +var sourceObj = undefined; +if (isNodeList(sourceObj)) { + sourceObj.length; +} +if (isHTMLCollection(sourceObj)) { + sourceObj.length; +} +if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { + sourceObj.length; +} diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index 286612ef4df9b..e47bdb19d346c 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -22,3 +22,72 @@ cond || (x = 0); x; // string | number >x : Symbol(x, Decl(controlFlowBinaryOrExpression.ts, 0, 3)) +export interface NodeList { +>NodeList : Symbol(NodeList, Decl(controlFlowBinaryOrExpression.ts, 8, 2)) + + length: number; +>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) +} + +export interface HTMLCollection { +>HTMLCollection : Symbol(HTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 12, 1)) + + length: number; +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +} + +declare function isNodeList(sourceObj: any): sourceObj is NodeList; +>isNodeList : Symbol(isNodeList, Decl(controlFlowBinaryOrExpression.ts, 16, 1)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 18, 28)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 18, 28)) +>NodeList : Symbol(NodeList, Decl(controlFlowBinaryOrExpression.ts, 8, 2)) + +declare function isHTMLCollection(sourceObj: any): sourceObj is HTMLCollection; +>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 19, 34)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 19, 34)) +>HTMLCollection : Symbol(HTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 12, 1)) + +type EventTargetLike = {a: string} | HTMLCollection | NodeList; +>EventTargetLike : Symbol(EventTargetLike, Decl(controlFlowBinaryOrExpression.ts, 19, 79)) +>a : Symbol(a, Decl(controlFlowBinaryOrExpression.ts, 21, 24)) +>HTMLCollection : Symbol(HTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 12, 1)) +>NodeList : Symbol(NodeList, Decl(controlFlowBinaryOrExpression.ts, 8, 2)) + +var sourceObj: EventTargetLike = undefined; +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) +>EventTargetLike : Symbol(EventTargetLike, Decl(controlFlowBinaryOrExpression.ts, 19, 79)) +>undefined : Symbol(undefined) + +if (isNodeList(sourceObj)) { +>isNodeList : Symbol(isNodeList, Decl(controlFlowBinaryOrExpression.ts, 16, 1)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) + + sourceObj.length; +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +} + +if (isHTMLCollection(sourceObj)) { +>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) + + sourceObj.length; +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +} + +if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { +>isNodeList : Symbol(isNodeList, Decl(controlFlowBinaryOrExpression.ts, 16, 1)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) +>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) + + sourceObj.length; +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +} + diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 404e153788afe..8c1cd32d8d9d7 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -35,3 +35,78 @@ cond || (x = 0); x; // string | number >x : string | number +export interface NodeList { +>NodeList : NodeList + + length: number; +>length : number +} + +export interface HTMLCollection { +>HTMLCollection : HTMLCollection + + length: number; +>length : number +} + +declare function isNodeList(sourceObj: any): sourceObj is NodeList; +>isNodeList : (sourceObj: any) => sourceObj is NodeList +>sourceObj : any +>sourceObj : any +>NodeList : NodeList + +declare function isHTMLCollection(sourceObj: any): sourceObj is HTMLCollection; +>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection +>sourceObj : any +>sourceObj : any +>HTMLCollection : HTMLCollection + +type EventTargetLike = {a: string} | HTMLCollection | NodeList; +>EventTargetLike : { a: string; } | HTMLCollection | NodeList +>a : string +>HTMLCollection : HTMLCollection +>NodeList : NodeList + +var sourceObj: EventTargetLike = undefined; +>sourceObj : { a: string; } | HTMLCollection | NodeList +>EventTargetLike : { a: string; } | HTMLCollection | NodeList +>undefined : any +>undefined : undefined + +if (isNodeList(sourceObj)) { +>isNodeList(sourceObj) : boolean +>isNodeList : (sourceObj: any) => sourceObj is NodeList +>sourceObj : { a: string; } | HTMLCollection + + sourceObj.length; +>sourceObj.length : number +>sourceObj : HTMLCollection +>length : number +} + +if (isHTMLCollection(sourceObj)) { +>isHTMLCollection(sourceObj) : boolean +>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection +>sourceObj : HTMLCollection | { a: string; } + + sourceObj.length; +>sourceObj.length : number +>sourceObj : HTMLCollection +>length : number +} + +if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { +>isNodeList(sourceObj) || isHTMLCollection(sourceObj) : boolean +>isNodeList(sourceObj) : boolean +>isNodeList : (sourceObj: any) => sourceObj is NodeList +>sourceObj : HTMLCollection | { a: string; } +>isHTMLCollection(sourceObj) : boolean +>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection +>sourceObj : { a: string; } + + sourceObj.length; +>sourceObj.length : number +>sourceObj : HTMLCollection +>length : number +} + diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 12b2d3260620a..5fa55aa6d88bb 100644 --- a/tests/baselines/reference/instanceOfAssignability.types +++ b/tests/baselines/reference/instanceOfAssignability.types @@ -133,8 +133,8 @@ function fn5(x: Derived1) { // 1.5: y: Derived1 // Want: ??? let y = x; ->y : Derived1 ->x : Derived1 +>y : {} +>x : {} } } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index a7f403e76eca1..30f60a5766fae 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -99,8 +99,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : {} +>x : {} } if (!hasKind(x, "B")) { @@ -116,6 +116,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : A ->x : A +>d : {} +>x : {} } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index edad220b0869d..58a22097864cc 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -93,8 +93,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : {} +>x : {} } if (!hasKind(x, "B")) { @@ -110,6 +110,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : A ->x : A +>d : {} +>x : {} } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index 258166593886b..a7cef3ec20342 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -96,8 +96,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : {} +>x : {} } if (!hasKind(x, "B")) { @@ -113,6 +113,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : A ->x : A +>d : {} +>x : {} } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt new file mode 100644 index 0000000000000..28cc85695bc68 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt @@ -0,0 +1,14 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20): error TS2339: Property 'global' does not exist on type '{}'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (1 errors) ==== + interface I { global: string; } + var result: I; + var result2: I; + + if (!(result instanceof RegExp)) { + result = result2; + } else if (!result.global) { + ~~~~~~ +!!! error TS2339: Property 'global' does not exist on type '{}'. + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols deleted file mode 100644 index cc2695e6aea90..0000000000000 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols +++ /dev/null @@ -1,26 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === -interface I { global: string; } ->I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) ->global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) - -var result: I; ->result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) ->I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) - -var result2: I; ->result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) ->I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) - -if (!(result instanceof RegExp)) { ->result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) ->RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) - - result = result2; ->result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) ->result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) - -} else if (!result.global) { ->result.global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) ->result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) ->global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) -} diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types deleted file mode 100644 index 0d7b477faedcd..0000000000000 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.types +++ /dev/null @@ -1,31 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === -interface I { global: string; } ->I : I ->global : string - -var result: I; ->result : I ->I : I - -var result2: I; ->result2 : I ->I : I - -if (!(result instanceof RegExp)) { ->!(result instanceof RegExp) : boolean ->(result instanceof RegExp) : boolean ->result instanceof RegExp : boolean ->result : I ->RegExp : RegExpConstructor - - result = result2; ->result = result2 : I ->result : I ->result2 : I - -} else if (!result.global) { ->!result.global : boolean ->result.global : string ->result : I ->global : string -} From 8a0bc3b992328adc2fd40efb52d7f0d1d81c3604 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 13:02:52 -0700 Subject: [PATCH 60/67] Support assignments in truthiness type guards --- src/compiler/binder.ts | 2 ++ src/compiler/checker.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 91f4247a67307..3d6db01580ac0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -617,6 +617,8 @@ namespace ts { function isNarrowingBinaryExpression(expr: BinaryExpression) { switch (expr.operatorToken.kind) { + case SyntaxKind.EqualsToken: + return isNarrowableReference(expr.left); case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 195245648a7e7..a3b3451fe35a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7671,6 +7671,8 @@ namespace ts { function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { switch (expr.operatorToken.kind) { + case SyntaxKind.EqualsToken: + return narrowTypeByTruthiness(type, expr.left, assumeTrue); case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: From d2b89be4bc6788d5d1f9367f1176aaf8f1118daa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 13:03:08 -0700 Subject: [PATCH 61/67] Adding test --- .../reference/controlFlowTruthiness.js | 134 +++++++++++++++ .../reference/controlFlowTruthiness.symbols | 143 ++++++++++++++++ .../reference/controlFlowTruthiness.types | 160 ++++++++++++++++++ .../controlFlow/controlFlowTruthiness.ts | 70 ++++++++ 4 files changed, 507 insertions(+) create mode 100644 tests/baselines/reference/controlFlowTruthiness.js create mode 100644 tests/baselines/reference/controlFlowTruthiness.symbols create mode 100644 tests/baselines/reference/controlFlowTruthiness.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowTruthiness.ts diff --git a/tests/baselines/reference/controlFlowTruthiness.js b/tests/baselines/reference/controlFlowTruthiness.js new file mode 100644 index 0000000000000..b595bcbd18096 --- /dev/null +++ b/tests/baselines/reference/controlFlowTruthiness.js @@ -0,0 +1,134 @@ +//// [controlFlowTruthiness.ts] + +declare function foo(): string | undefined; + +function f1() { + let x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} + +function f2() { + let x: string | undefined; + x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} + +function f3() { + let x: string | undefined; + if (x = foo()) { + x; // string + } + else { + x; // string | undefined + } +} + +function f4() { + let x: string | undefined; + if (!(x = foo())) { + x; // string | undefined + } + else { + x; // string + } +} + +function f5() { + let x: string | undefined; + let y: string | undefined; + if (x = y = foo()) { + x; // string + y; // string | undefined + } + else { + x; // string | undefined + y; // string | undefined + } +} + +function f6() { + let x: string | undefined; + let y: string | undefined; + if (x = foo(), y = foo()) { + x; // string | undefined + y; // string + } + else { + x; // string | undefined + y; // string | undefined + } +} + + +//// [controlFlowTruthiness.js] +function f1() { + var x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} +function f2() { + var x; + x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} +function f3() { + var x; + if (x = foo()) { + x; // string + } + else { + x; // string | undefined + } +} +function f4() { + var x; + if (!(x = foo())) { + x; // string | undefined + } + else { + x; // string + } +} +function f5() { + var x; + var y; + if (x = y = foo()) { + x; // string + y; // string | undefined + } + else { + x; // string | undefined + y; // string | undefined + } +} +function f6() { + var x; + var y; + if (x = foo(), y = foo()) { + x; // string | undefined + y; // string + } + else { + x; // string | undefined + y; // string | undefined + } +} diff --git a/tests/baselines/reference/controlFlowTruthiness.symbols b/tests/baselines/reference/controlFlowTruthiness.symbols new file mode 100644 index 0000000000000..37c1fb135f2c0 --- /dev/null +++ b/tests/baselines/reference/controlFlowTruthiness.symbols @@ -0,0 +1,143 @@ +=== tests/cases/conformance/controlFlow/controlFlowTruthiness.ts === + +declare function foo(): string | undefined; +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + +function f1() { +>f1 : Symbol(f1, Decl(controlFlowTruthiness.ts, 1, 43)) + + let x = foo(); +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 4, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + if (x) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 4, 7)) + + x; // string +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 4, 7)) + } + else { + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 4, 7)) + } +} + +function f2() { +>f2 : Symbol(f2, Decl(controlFlowTruthiness.ts, 11, 1)) + + let x: string | undefined; +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 14, 7)) + + x = foo(); +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 14, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + if (x) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 14, 7)) + + x; // string +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 14, 7)) + } + else { + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 14, 7)) + } +} + +function f3() { +>f3 : Symbol(f3, Decl(controlFlowTruthiness.ts, 22, 1)) + + let x: string | undefined; +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 25, 7)) + + if (x = foo()) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 25, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + x; // string +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 25, 7)) + } + else { + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 25, 7)) + } +} + +function f4() { +>f4 : Symbol(f4, Decl(controlFlowTruthiness.ts, 32, 1)) + + let x: string | undefined; +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 35, 7)) + + if (!(x = foo())) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 35, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 35, 7)) + } + else { + x; // string +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 35, 7)) + } +} + +function f5() { +>f5 : Symbol(f5, Decl(controlFlowTruthiness.ts, 42, 1)) + + let x: string | undefined; +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 45, 7)) + + let y: string | undefined; +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 46, 7)) + + if (x = y = foo()) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 45, 7)) +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 46, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + x; // string +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 45, 7)) + + y; // string | undefined +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 46, 7)) + } + else { + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 45, 7)) + + y; // string | undefined +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 46, 7)) + } +} + +function f6() { +>f6 : Symbol(f6, Decl(controlFlowTruthiness.ts, 55, 1)) + + let x: string | undefined; +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 58, 7)) + + let y: string | undefined; +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 59, 7)) + + if (x = foo(), y = foo()) { +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 58, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 59, 7)) +>foo : Symbol(foo, Decl(controlFlowTruthiness.ts, 0, 0)) + + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 58, 7)) + + y; // string +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 59, 7)) + } + else { + x; // string | undefined +>x : Symbol(x, Decl(controlFlowTruthiness.ts, 58, 7)) + + y; // string | undefined +>y : Symbol(y, Decl(controlFlowTruthiness.ts, 59, 7)) + } +} + diff --git a/tests/baselines/reference/controlFlowTruthiness.types b/tests/baselines/reference/controlFlowTruthiness.types new file mode 100644 index 0000000000000..2d35856fe8340 --- /dev/null +++ b/tests/baselines/reference/controlFlowTruthiness.types @@ -0,0 +1,160 @@ +=== tests/cases/conformance/controlFlow/controlFlowTruthiness.ts === + +declare function foo(): string | undefined; +>foo : () => string | undefined + +function f1() { +>f1 : () => void + + let x = foo(); +>x : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + if (x) { +>x : string | undefined + + x; // string +>x : string + } + else { + x; // string | undefined +>x : string | undefined + } +} + +function f2() { +>f2 : () => void + + let x: string | undefined; +>x : string | undefined + + x = foo(); +>x = foo() : string | undefined +>x : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + if (x) { +>x : string | undefined + + x; // string +>x : string + } + else { + x; // string | undefined +>x : string | undefined + } +} + +function f3() { +>f3 : () => void + + let x: string | undefined; +>x : string | undefined + + if (x = foo()) { +>x = foo() : string | undefined +>x : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + x; // string +>x : string + } + else { + x; // string | undefined +>x : string | undefined + } +} + +function f4() { +>f4 : () => void + + let x: string | undefined; +>x : string | undefined + + if (!(x = foo())) { +>!(x = foo()) : boolean +>(x = foo()) : string | undefined +>x = foo() : string | undefined +>x : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + x; // string | undefined +>x : string | undefined + } + else { + x; // string +>x : string + } +} + +function f5() { +>f5 : () => void + + let x: string | undefined; +>x : string | undefined + + let y: string | undefined; +>y : string | undefined + + if (x = y = foo()) { +>x = y = foo() : string | undefined +>x : string | undefined +>y = foo() : string | undefined +>y : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + x; // string +>x : string + + y; // string | undefined +>y : string | undefined + } + else { + x; // string | undefined +>x : string | undefined + + y; // string | undefined +>y : string | undefined + } +} + +function f6() { +>f6 : () => void + + let x: string | undefined; +>x : string | undefined + + let y: string | undefined; +>y : string | undefined + + if (x = foo(), y = foo()) { +>x = foo(), y = foo() : string | undefined +>x = foo() : string | undefined +>x : string | undefined +>foo() : string | undefined +>foo : () => string | undefined +>y = foo() : string | undefined +>y : string | undefined +>foo() : string | undefined +>foo : () => string | undefined + + x; // string | undefined +>x : string | undefined + + y; // string +>y : string + } + else { + x; // string | undefined +>x : string | undefined + + y; // string | undefined +>y : string | undefined + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowTruthiness.ts b/tests/cases/conformance/controlFlow/controlFlowTruthiness.ts new file mode 100644 index 0000000000000..ba1947da13b9a --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowTruthiness.ts @@ -0,0 +1,70 @@ +// @strictNullChecks: true + +declare function foo(): string | undefined; + +function f1() { + let x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} + +function f2() { + let x: string | undefined; + x = foo(); + if (x) { + x; // string + } + else { + x; // string | undefined + } +} + +function f3() { + let x: string | undefined; + if (x = foo()) { + x; // string + } + else { + x; // string | undefined + } +} + +function f4() { + let x: string | undefined; + if (!(x = foo())) { + x; // string | undefined + } + else { + x; // string + } +} + +function f5() { + let x: string | undefined; + let y: string | undefined; + if (x = y = foo()) { + x; // string + y; // string | undefined + } + else { + x; // string | undefined + y; // string | undefined + } +} + +function f6() { + let x: string | undefined; + let y: string | undefined; + if (x = foo(), y = foo()) { + x; // string | undefined + y; // string + } + else { + x; // string | undefined + y; // string | undefined + } +} From ab4b03983b049adcc2b9cb4cf522b0ebaf8605f6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 13:40:57 -0700 Subject: [PATCH 62/67] Removing unused logic --- src/compiler/checker.ts | 21 +++++---------------- src/compiler/types.ts | 5 ----- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a3b3451fe35a3..6bffe78bc7570 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7197,19 +7197,7 @@ namespace ts { // EXPRESSION TYPE CHECKING - function createTransientIdentifier(symbol: Symbol, location: Node): Identifier { - const result = createNode(SyntaxKind.Identifier); - result.text = symbol.name; - result.resolvedSymbol = symbol; - result.parent = location; - result.id = -1; - return result; - } - function getResolvedSymbol(node: Identifier): Symbol { - if (node.id === -1) { - return (node).resolvedSymbol; - } const links = getNodeLinks(node); if (!links.resolvedSymbol) { links.resolvedSymbol = !nodeIsMissing(node) && resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node) || unknownSymbol; @@ -7867,7 +7855,7 @@ namespace ts { } // If location is an identifier or property access that references the given // symbol, use the location as the reference with respect to which we narrow. - if (isExpression(location)) { + if (isExpression(location) && !isAssignmentTarget(location)) { checkExpression(location); if (getNodeLinks(location).resolvedSymbol === symbol) { return getNarrowedTypeOfReference(type, location); @@ -7876,9 +7864,10 @@ namespace ts { } // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference - // to it at the given location. To answer that question we manufacture a transient - // identifier at the location and narrow with respect to that identifier. - return getNarrowedTypeOfReference(type, createTransientIdentifier(symbol, location)); + // to it at the given location. Since we have no control flow information for the + // hypotherical reference (control flow information is created and attached by the + // binder), we simply return the declared type of the symbol. + return type; } function skipParenthesizedNodes(expression: Expression): Expression { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b5e33d5d361a4..0a4de847c0096 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -479,11 +479,6 @@ namespace ts { originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later } - // Transient identifier node (marked by id === -1) - export interface TransientIdentifier extends Identifier { - resolvedSymbol: Symbol; - } - // @kind(SyntaxKind.QualifiedName) export interface QualifiedName extends Node { // Must have same layout as PropertyAccess From e12b2a7d276b82ab803e0c1d0e06a687bf557fb7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 16:06:05 -0700 Subject: [PATCH 63/67] Correct issue with exported variables in code flow analysis --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6bffe78bc7570..ce17db0bd9a39 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7576,7 +7576,7 @@ namespace ts { // 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 && - getResolvedSymbol(reference) === getSymbolOfNode(node)) { + getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(reference)) === getSymbolOfNode(node)) { return declaredType.flags & TypeFlags.Union ? getAssignmentReducedType(declaredType, getInitialType(node)) : declaredType; @@ -7857,7 +7857,7 @@ namespace ts { // symbol, use the location as the reference with respect to which we narrow. if (isExpression(location) && !isAssignmentTarget(location)) { checkExpression(location); - if (getNodeLinks(location).resolvedSymbol === symbol) { + if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) { return getNarrowedTypeOfReference(type, location); } } @@ -9745,7 +9745,7 @@ namespace ts { } const propType = getTypeOfSymbol(prop); - return node.kind === SyntaxKind.PropertyAccessExpression && prop.flags & SymbolFlags.Property && !isAssignmentTarget(node) ? + return node.kind === SyntaxKind.PropertyAccessExpression && prop.flags & (SymbolFlags.Variable | SymbolFlags.Property) && !isAssignmentTarget(node) ? getNarrowedTypeOfReference(propType, node) : propType; } From 4fb31acf2f52b37a5f3145f52ea4c4495e6f4b26 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Apr 2016 16:06:21 -0700 Subject: [PATCH 64/67] Update fourslash test --- .../fourslash/quickInfoOnNarrowedTypeInModule.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts index b2826d016509e..1f95de35403f6 100644 --- a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts +++ b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts @@ -38,7 +38,19 @@ goTo.marker('3'); verify.quickInfoIs('var nonExportedStrOrNum: string'); verify.completionListContains("nonExportedStrOrNum", "var nonExportedStrOrNum: string"); -['4', '5', '6', '7', '8', '9'].forEach((marker, index, arr) => { +goTo.marker('4'); +verify.quickInfoIs('var m.exportedStrOrNum: string | number'); +verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number"); + +goTo.marker('5'); +verify.quickInfoIs('var m.exportedStrOrNum: number'); +verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number"); + +goTo.marker('6'); +verify.quickInfoIs('var m.exportedStrOrNum: string'); +verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string"); + +['7', '8', '9'].forEach((marker, index, arr) => { goTo.marker(marker); verify.quickInfoIs('var m.exportedStrOrNum: string | number'); verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number"); From f06d3f698193821951717e5d5d92057f1b35fd70 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 22 Apr 2016 06:52:14 -0700 Subject: [PATCH 65/67] Only narrow to {} in getNarrowedType when types are completely unrelated --- src/compiler/checker.ts | 52 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ce17db0bd9a39..583f6c82b32e2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7327,18 +7327,6 @@ namespace ts { return false; } - function typeMaybeSubtypeOf(source: Type, target: Type) { - if (!(source.flags & TypeFlags.Union)) { - return isTypeSubtypeOf(source, target); - } - for (const t of (source).types) { - if (isTypeSubtypeOf(t, target)) { - return true; - } - } - return false; - } - // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. @@ -7769,28 +7757,26 @@ namespace ts { } function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { - if (typeMaybeSubtypeOf(type, candidate)) { - // If the current type, or a constituent of the current type, is a subtype of - // the candidate type then the current type contains the most specific information. - // and we simply filter out constituents that aren't applicable. For example, - // if the current type is string | string[] and the candidate type is any[], - // we filter out string. + if (!assumeTrue) { return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, t => isTypeSubtypeOf(t, candidate) === assumeTrue)) : - assumeTrue ? type : emptyUnionType; - } - if (assumeTrue) { - // In the true branch of the remaining cases, if the candidate type is assignable - // to the current type, then we narrow to the candidate type. Otherwise, we narrow - // to an empty type (because we now know the types are completely unrelated). For - // example, if the current type is Object and the candidate type is string[], we - // narrow to string[]. But if the current type is string and the candidate is - // string[], we narrow to the empty type. - const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : emptyUnionType; - } - // In the false branch of the remaining cases we leave the type unchanged. - return type; + getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, candidate))) : + type; + } + // If the current type is a union type, remove all constituents that aren't assignable to + // the candidate type. If one or more constituents remain, return a union of those. + if (type.flags & TypeFlags.Union) { + const assignableConstituents = filter((type).types, t => isTypeAssignableTo(t, candidate)); + if (assignableConstituents.length) { + return getUnionType(assignableConstituents); + } + } + // If the candidate type is assignable to the target type, narrow to the candidate type. + // Otherwise, if the current type is assignable to the candidate, keep the current type. + // Otherwise, the types are completely unrelated, so narrow to the empty type. + const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; + return isTypeAssignableTo(candidate, targetType) ? candidate : + isTypeAssignableTo(type, candidate) ? type : + emptyUnionType; } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { From 42e3fc4303f3a74a23e43fb12c0e94cbe37cc8dc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 22 Apr 2016 06:53:01 -0700 Subject: [PATCH 66/67] Revert previous change --- src/compiler/emitter.ts | 2 +- src/compiler/utilities.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 19c47db2193c9..254b8d7e61b17 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -7084,7 +7084,7 @@ const _super = (function (geti, seti) { hoistedVars = []; } - hoistedVars.push((node).name); + hoistedVars.push(node.name); return; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 65405db79aec2..60b041d843c88 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1178,7 +1178,7 @@ namespace ts { return ((node).moduleReference).expression; } - export function isInternalModuleImportEqualsDeclaration(node: Node): boolean { + export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind !== SyntaxKind.ExternalModuleReference; } From 0dee5addf3b129d9241e7dd57e895cea8ec7dfac Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 22 Apr 2016 06:53:28 -0700 Subject: [PATCH 67/67] Accepting new baselines --- tests/baselines/reference/stringLiteralTypesAsTags01.types | 4 ++-- tests/baselines/reference/stringLiteralTypesAsTags02.types | 4 ++-- tests/baselines/reference/stringLiteralTypesAsTags03.types | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index 30f60a5766fae..243b55c44192f 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -99,8 +99,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : {} ->x : {} +>b : A +>x : A } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index 58a22097864cc..20834e1dfef57 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -93,8 +93,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : {} ->x : {} +>b : A +>x : A } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index a7cef3ec20342..78ad260b71949 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -96,8 +96,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : {} ->x : {} +>b : A +>x : A } if (!hasKind(x, "B")) {