diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6957be484f35a..df4947b69cd37 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -17,14 +17,58 @@ namespace ts { Unreachable = 1 << 2, ReportedUnreachable = 1 << 3 } + interface Flow { + reachability: Reachability; + previous: FlowMarkerTarget[]; + } + interface Label { + node: Node; + continueFlow: Flow; + breakFlow: Flow; + } - function or(state1: Reachability, state2: Reachability): Reachability { - return (state1 | state2) & Reachability.Reachable - ? Reachability.Reachable - : (state1 & state2) & Reachability.ReportedUnreachable - ? Reachability.ReportedUnreachable - : Reachability.Unreachable; + function or(state1: Flow, state2: Flow): Flow { + let reachable = false; + let previous: FlowMarkerTarget[] = []; + if (state1.reachability === Reachability.Reachable) { + reachable = true; + previous = state1.previous; + } + if (state2.reachability === Reachability.Reachable) { + reachable = true; + if (state2.previous === undefined) { + previous = undefined; + } else if (previous !== undefined) { + previous = [...previous, ...state2.previous]; + } + } + if (reachable) { + return { + reachability: Reachability.Reachable, + previous + }; + } + + const reachability = (state1.reachability & state2.reachability) & Reachability.ReportedUnreachable + ? Reachability.ReportedUnreachable + : Reachability.Unreachable; + return { + reachability, + previous: undefined + }; } + const defaultUnreachable: Flow = { + reachability: Reachability.Unreachable, + previous: undefined + }; + const defaultReachable: Flow = { + reachability: Reachability.Reachable, + previous: undefined + }; + const defaultUninitialized: Flow = { + reachability: Reachability.Uninitialized, + previous: undefined + }; export function getModuleInstanceState(node: Node): ModuleInstanceState { // A module is uninstantiated if it contains only @@ -112,8 +156,9 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; - let currentReachabilityState: Reachability; - let labelStack: Reachability[]; + let currentReachabilityState: Flow = defaultReachable; + let labelStack: Label[]; + let lastImplicitLabel: [Label, Node]; let labelIndexMap: Map; let implicitLabels: number[]; @@ -128,6 +173,7 @@ namespace ts { // not depending on if we see "use strict" in certain places (or if we hit a class/namespace). let inStrictMode: boolean; + let branchFlowId = 0; let symbolCount = 0; let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; @@ -436,8 +482,8 @@ namespace ts { blockScopeContainer.locals = undefined; } - let savedReachabilityState: Reachability; - let savedLabelStack: Reachability[]; + let savedReachabilityState: Flow; + let savedLabelStack: Label[]; let savedLabels: Map; let savedImplicitLabels: number[]; let savedHasExplicitReturn: boolean; @@ -463,7 +509,19 @@ namespace ts { savedImplicitLabels = implicitLabels; savedHasExplicitReturn = hasExplicitReturn; - currentReachabilityState = Reachability.Reachable; + /* + * This allows narrowing in nested functions: + * let x: string | number; + * if (typeof x === "number") { + * let f = (y: number) => x * y; + * } + * Since you cannot always know where `f` can be invoked, this narrowing is not entirely sound. + * Though, removing this would be too strict and a breaking change. + */ + currentReachabilityState = { + reachability: Reachability.Reachable, + previous: currentReachabilityState.previous + }; hasExplicitReturn = false; labelStack = labelIndexMap = implicitLabels = undefined; } @@ -474,7 +532,7 @@ namespace ts { bindReachableStatement(node); - if (currentReachabilityState === Reachability.Reachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { + if (currentReachabilityState.reachability === Reachability.Reachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) { flags |= NodeFlags.HasExplicitReturn; @@ -562,6 +620,12 @@ namespace ts { case SyntaxKind.LabeledStatement: bindLabeledStatement(node); break; + case SyntaxKind.BinaryExpression: + bindBinaryExpression(node); + break; + case SyntaxKind.ConditionalExpression: + bindConditionalExpression(node); + break; default: forEachChild(node, bind); break; @@ -569,86 +633,112 @@ namespace ts { } 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; + bindFlowMarker(n); - // bind expressions (don't affect reachability) + // bind expressions (don't affect reachability, do affect identifier flow) bind(n.expression); + const preWhileState = + n.expression.kind === SyntaxKind.FalseKeyword ? defaultUnreachable : currentReachabilityState; + const postWhileState = + n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : currentReachabilityState; + currentReachabilityState = preWhileState; - const postWhileLabel = pushImplicitLabel(); + const postWhileLabel = pushImplicitLabel(n); + bindBranchFlow(n, n.expression, true); bind(n.statement); - popImplicitLabel(postWhileLabel, postWhileState); + + const postBodyState = currentReachabilityState; + bindIterationFlowMarkerEnd(n); + + currentReachabilityState = postWhileState; + bindBranchFlow(n, n.expression, false); + + popImplicitLabel(postWhileLabel, currentReachabilityState, n); } function bindDoStatement(n: DoStatement): void { - const preDoState = currentReachabilityState; + bindFlowMarker(n); - const postDoLabel = pushImplicitLabel(); + const postDoLabel = pushImplicitLabel(n); 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); + bind(n.expression, true); + + const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : currentReachabilityState; + + bindBranchFlow(n, n.expression, true); + bindIterationFlowMarkerEnd(n); + + currentReachabilityState = postDoState; + bindBranchFlow(n, n.expression, false); + popImplicitLabel(postDoLabel, currentReachabilityState, n.expression); } function bindForStatement(n: ForStatement): void { + // bind expressions (don't affect reachability, do affect identifier flow) + bind(n.initializer); + const postInitializer = currentReachabilityState; + const loopStartNode = n.condition || n.statement; + + bind(n.condition, loopStartNode === n.condition); + const preForState = currentReachabilityState; - const postForLabel = pushImplicitLabel(); + const postForLabel = pushImplicitLabel(n); - // bind expressions (don't affect reachability) - bind(n.initializer); - bind(n.condition); - bind(n.incrementor); + if (n.condition) bindBranchFlow(n, n.condition, true); + bind(n.statement, loopStartNode === n.statement); - bind(n.statement); + if (n.incrementor) { + bind(n.incrementor, true); + } + bindIterationFlowMarkerEnd(loopStartNode); // 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); + currentReachabilityState = isInfiniteLoop ? defaultUnreachable : preForState; + if (n.condition) bindBranchFlow(n, n.condition, false); + popImplicitLabel(postForLabel, currentReachabilityState, n.incrementor || loopStartNode); } function bindForInOrForOfStatement(n: ForInStatement | ForOfStatement): void { + // bind expressions (don't affect reachability, do affect identfier flow) + bind(n.expression); + const preInitializerState = currentReachabilityState; + bind(n.initializer, true); const preStatementState = currentReachabilityState; - const postStatementLabel = pushImplicitLabel(); - // bind expressions (don't affect reachability) - bind(n.initializer); - bind(n.expression); + const postStatementLabel = pushImplicitLabel(n); bind(n.statement); - popImplicitLabel(postStatementLabel, preStatementState); + bindIterationFlowMarkerEnd(n.initializer); + popImplicitLabel(postStatementLabel, or(preInitializerState, preStatementState), n.initializer); } function bindIfStatement(n: IfStatement): void { + // bind expression (don't affect reachability, do affect identifier flow) + bind(n.expression); + // 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; + const ifTrueState = n.expression.kind === SyntaxKind.FalseKeyword ? defaultUnreachable : 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; + const ifFalseState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : currentReachabilityState; currentReachabilityState = ifTrueState; - - // bind expression (don't affect reachability) - bind(n.expression); + bindBranchFlow(n, n.expression, true); bind(n.thenStatement); - if (n.elseStatement) { - const preElseState = currentReachabilityState; - currentReachabilityState = ifFalseState; - bind(n.elseStatement); - currentReachabilityState = or(currentReachabilityState, preElseState); - } - else { - currentReachabilityState = or(currentReachabilityState, ifFalseState); - } + + const preElseState = currentReachabilityState; + currentReachabilityState = ifFalseState; + bindBranchFlow(n, n.expression, false); + if (n.elseStatement) bind(n.elseStatement); + currentReachabilityState = or(currentReachabilityState, preElseState); } function bindReturnOrThrow(n: ReturnStatement | ThrowStatement): void { @@ -657,22 +747,27 @@ namespace ts { if (n.kind === SyntaxKind.ReturnStatement) { hasExplicitReturn = true; } - currentReachabilityState = Reachability.Unreachable; + currentReachabilityState = defaultUnreachable; } 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); + const isValidJump = jumpToLabel(n.label, n.kind === SyntaxKind.BreakStatement ? currentReachabilityState : defaultUnreachable, n.kind === SyntaxKind.ContinueStatement); if (isValidJump) { - currentReachabilityState = Reachability.Unreachable; + currentReachabilityState = defaultUnreachable; } } function bindTryStatement(n: TryStatement): void { - // catch\finally blocks has the same reachability as try block - const preTryState = currentReachabilityState; + const preTryState: Flow = { + // catch\finally blocks has the same reachability as try block + reachability: currentReachabilityState.reachability, + // Control flow in the catch and finally block is hard, + // so we fallback to initial type of the variable + previous: undefined + }; bind(n.tryBlock); const postTryState = currentReachabilityState; @@ -690,18 +785,18 @@ namespace ts { } function bindSwitchStatement(n: SwitchStatement): void { - const preSwitchState = currentReachabilityState; - const postSwitchLabel = pushImplicitLabel(); - - // bind expression (don't affect reachability) + // bind expression (don't affect reachability, do affect identfier flow) bind(n.expression); + const preSwitchState = currentReachabilityState; + const postSwitchLabel = pushImplicitLabel(n); + bind(n.caseBlock); const hasDefault = forEach(n.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); - // 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; + // 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 !== Reachability.Reachable ? defaultUnreachable : preSwitchState; popImplicitLabel(postSwitchLabel, postSwitchState); } @@ -715,7 +810,7 @@ namespace ts { bind(clause); if (clause.statements.length && i !== n.clauses.length - 1 && // allow fallthrough from the last case - currentReachabilityState === Reachability.Reachable && + currentReachabilityState.reachability === Reachability.Reachable && options.noFallthroughCasesInSwitch) { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } @@ -723,16 +818,55 @@ namespace ts { } function bindLabeledStatement(n: LabeledStatement): void { + bindFlowMarker(n); + // call bind on label (don't affect reachability) bind(n.label); - const ok = pushNamedLabel(n.label); + const ok = pushNamedLabel(n.label, n); bind(n.statement); if (ok) { popNamedLabel(n.label, currentReachabilityState); } } + function bindBinaryExpression(node: BinaryExpression) { + const isAnd = node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken; + const isOr = node.operatorToken.kind === SyntaxKind.BarBarToken; + + if (isAnd || isOr) { + bindFlowMarker(node); + bind(node.left); + const preRightState = currentReachabilityState; + bindBranchFlow(node, node.left, isAnd); + bind(node.right); + currentReachabilityState = or(currentReachabilityState, preRightState); + } + else if (isAssignmentOperator(node.operatorToken.kind) && (node.left.kind === SyntaxKind.Identifier || node.left.kind === SyntaxKind.ArrayLiteralExpression || node.left.kind === SyntaxKind.ObjectLiteralExpression)) { + bind(node.right); + bind(node.left); + } + else { + forEachChild(node, bind); + } + } + + function bindConditionalExpression(node: ConditionalExpression) { + bindFlowMarker(node); + bind(node.condition); + const postConditionState = currentReachabilityState; + + bindBranchFlow(node, node.condition, true); + bind(node.whenTrue); + const postTrueState = currentReachabilityState; + + currentReachabilityState = postConditionState; + bindBranchFlow(node, node.condition, false); + bind(node.whenFalse); + + currentReachabilityState = or(postTrueState, currentReachabilityState); + } + function getContainerFlags(node: Node): ContainerFlags { switch (node.kind) { case SyntaxKind.ClassExpression: @@ -1161,12 +1295,16 @@ namespace ts { return "__" + indexOf((node.parent).parameters, node); } - function bind(node: Node): void { + function bind(node: Node, flowMarker?: boolean): void { if (!node) { return; } node.parent = parent; + node.previous = currentReachabilityState.previous; + if (flowMarker) { + bindFlowMarker(node); + } const savedInStrictMode = inStrictMode; if (!savedInStrictMode) { @@ -1187,7 +1325,7 @@ namespace ts { // Then we recurse into the children of the node to bind them as well. For certain // symbols we do specialized work when we recurse. For example, we'll keep track of - // the current 'container' node when it changes. This helps us know which symbol table + // the current 'container' node when it changes. This helps us know which symbol table // a local should go into for example. bindChildren(node); @@ -1238,8 +1376,12 @@ namespace ts { function bindWorker(node: Node) { switch (node.kind) { /* Strict mode checks */ + case SyntaxKind.ThisKeyword: + return bindThisExpression(node); case SyntaxKind.Identifier: - return checkStrictModeIdentifier(node); + return bindIdentifier(node); + case SyntaxKind.PropertyAccessExpression: + return bindPropertyAccessExpression(node); case SyntaxKind.BinaryExpression: if (isInJavaScriptFile(node)) { const specialKind = getSpecialPropertyAssignmentKind(node); @@ -1661,37 +1803,100 @@ namespace ts { : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } + function bindThisExpression(node: Node) { + bindFlowMarker(node); + } + function bindIdentifier(node: Identifier) { + checkStrictModeIdentifier(node); + if (isExpression(node) + || (node.parent.kind === SyntaxKind.VariableDeclaration && (node.parent).name === node) + || (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment && (node.parent).name === node)) { + + if (node.parent.kind === SyntaxKind.PropertyAccessExpression && ( node.parent).name === node) { + return; + } + bindFlowMarker(node); + } + } + function bindPropertyAccessExpression(node: PropertyAccessExpression) { + bindFlowMarker(node); + } + + function bindFlowMarker(node: Node) { + currentReachabilityState = { + reachability: currentReachabilityState.reachability, + previous: [node] + }; + } + function bindIterationFlowMarkerEnd(node: Node) { + if (node.previous !== undefined && currentReachabilityState.reachability === Reachability.Reachable) { + if (currentReachabilityState.previous === undefined) { + node.previous = undefined; + } + else { + node.previous = [...node.previous, ...currentReachabilityState.previous]; + } + } + } + function bindBranchFlow(node: BranchFlowNode, expression: Expression, trueBranch: boolean) { + const branchFlow: BranchFlow = { + id: branchFlowId++, + previous: currentReachabilityState.previous, + node, + expression, + trueBranch + }; + currentReachabilityState = { + reachability: currentReachabilityState.reachability, + previous: [branchFlow] + }; + } + // reachability checks - function pushNamedLabel(name: Identifier): boolean { + function pushLabel(node: Node) { + return labelStack.push({ continueFlow: defaultUninitialized, breakFlow: defaultUninitialized, node }) - 1; + } + function pushNamedLabel(name: Identifier, node: LabeledStatement): boolean { initializeReachabilityStateIfNecessary(); if (hasProperty(labelIndexMap, name.text)) { return false; } - labelIndexMap[name.text] = labelStack.push(Reachability.Uninitialized) - 1; + labelIndexMap[name.text] = pushLabel(node); return true; } - function pushImplicitLabel(): number { + function pushImplicitLabel(node: Node): number { initializeReachabilityStateIfNecessary(); - const index = labelStack.push(Reachability.Uninitialized) - 1; + const index = pushLabel(node); implicitLabels.push(index); return index; } - function popNamedLabel(label: Identifier, outerState: Reachability): void { + function popNamedLabel(label: Identifier, outerState: Flow): 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); + const labelState = labelStack.pop(); + if (!options.allowUnusedLabels + && labelState.breakFlow.reachability === Reachability.Uninitialized + && labelState.continueFlow.reachability === Reachability.Uninitialized) { + + file.bindDiagnostics.push(createDiagnosticForNode(label, Diagnostics.Unused_label)); + } + let continueToNode: Node = undefined; + if (lastImplicitLabel !== undefined && lastImplicitLabel[0].node === (labelState.node).statement) { + continueToNode = lastImplicitLabel[1]; + } + setCurrentStateAtLabel(labelState, outerState, continueToNode); } - function popImplicitLabel(implicitLabelIndex: number, outerState: Reachability): void { + function popImplicitLabel(implicitLabelIndex: number, outerState: Flow, continueToNode?: Node): void { if (labelStack.length !== implicitLabelIndex + 1) { Debug.assert(false, `Label stack: ${labelStack.length}, index:${implicitLabelIndex}`); } @@ -1702,22 +1907,23 @@ namespace ts { Debug.assert(false, `i: ${i}, index: ${implicitLabelIndex}`); } - setCurrentStateAtLabel(labelStack.pop(), outerState, /*name*/ undefined); + const labelState = labelStack.pop(); + setCurrentStateAtLabel(labelState, outerState, continueToNode); + lastImplicitLabel = [labelState, continueToNode]; } - 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)); + function setCurrentStateAtLabel(label: Label, outerState: Flow, continueToNode?: Node) { + currentReachabilityState = or(label.breakFlow, outerState); + if (label.continueFlow.reachability === Reachability.Reachable && continueToNode !== undefined && continueToNode.previous !== undefined) { + if (label.continueFlow.previous === undefined) { + continueToNode.previous = undefined; + } else { + continueToNode.previous = [...continueToNode.previous, ...label.continueFlow.previous]; } - currentReachabilityState = outerState; - } - else { - currentReachabilityState = or(innerMergedState, outerState); } } - function jumpToLabel(label: Identifier, outerState: Reachability): boolean { + function jumpToLabel(label: Identifier, outerState: Flow, isContinue: boolean): boolean { initializeReachabilityStateIfNecessary(); const index = label ? labelIndexMap[label.text] : lastOrUndefined(implicitLabels); @@ -1726,13 +1932,18 @@ namespace ts { // break/continue used outside of loops return false; } - const stateAtLabel = labelStack[index]; - labelStack[index] = stateAtLabel === Reachability.Uninitialized ? outerState : or(stateAtLabel, outerState); + const state = labelStack[index]; + if (isContinue) { + // node is iteration + state.continueFlow = or(state.continueFlow, currentReachabilityState); + } else { + state.breakFlow = or(state.breakFlow, currentReachabilityState); + } return true; } function checkUnreachable(node: Node): boolean { - switch (currentReachabilityState) { + switch (currentReachabilityState.reachability) { case Reachability.Unreachable: const reportError = // report error on all statements except empty ones @@ -1745,7 +1956,10 @@ namespace ts { (node.kind === SyntaxKind.EnumDeclaration && (!isConstEnumDeclaration(node) || options.preserveConstEnums)); if (reportError) { - currentReachabilityState = Reachability.ReportedUnreachable; + currentReachabilityState = { + reachability: Reachability.ReportedUnreachable, + previous: currentReachabilityState.previous + }; // unreachable code is reported if // - user has explicitly asked about it AND @@ -1785,7 +1999,7 @@ namespace ts { if (labelIndexMap) { return; } - currentReachabilityState = Reachability.Reachable; + currentReachabilityState = currentReachabilityState || defaultReachable; labelIndexMap = {}; labelStack = []; implicitLabels = []; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92c5065b7dd8f..f9f4dbe056bbe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2682,7 +2682,7 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false); + const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, true); if (!declaration.dotDotDotToken) { // Use specific property type when parent is a tuple or numeric index type when parent is an array const propName = "" + indexOf(pattern.elements, declaration); @@ -2770,7 +2770,7 @@ namespace ts { // missing properties/signatures required to get its iteratedType (like // [Symbol.iterator] or next). This may be because we accessed properties from anyType, // or it may have led to an error inside getElementTypeOfIterable. - return checkRightHandSideOfForOf((declaration.parent.parent).expression) || anyType; + return checkRightHandSideOfForOf((declaration.parent.parent).expression, true) || anyType; } if (isBindingPattern(declaration.parent)) { @@ -7103,8 +7103,12 @@ namespace ts { result.resolvedSymbol = symbol; result.parent = location; result.id = -1; + result.previous = [location]; return result; } + function cleanTransientIdentifier() { + nodeLinks[-1] = undefined; + } function getResolvedSymbol(node: Identifier): Symbol { if (node.id === -1) { @@ -7299,93 +7303,247 @@ namespace ts { return false; } + function getPreviousOccurences(reference: Node, where: Node, callback: (node: Node, guards: BranchFlow[], isSameSymbol: boolean) => boolean) { + let stop = false; + const visited: { [id: number]: boolean } = {}; + const guards: BranchFlow[] = []; + + const stack: [FlowMarkerTarget, boolean][] = [[where, true]]; + while (stack.length !== 0) { + const [location, isStart] = stack.pop(); + if (!isStart) { + if ((location).kind !== undefined) { + visited[getNodeId(location)] = false; + } + else { + guards.pop(); + } + continue; + } + + let isGuard = false; + let nodeId: number; + if ((location).kind !== undefined) { + nodeId = getNodeId(location); + if (visited[nodeId]) continue; + + let same = isSameSymbol(reference, location); + let previousLocation = location; + if (isLeftHandSideOfAssignment(location)) { + let node = reference; + while (node.kind === SyntaxKind.PropertyAccessExpression) { + node = (node).expression; + if (isSameSymbol(node, location)) { + // Reference node was part of an assignment + // Example: a.b.c is reference, a or a.b was assigned + previousLocation = undefined; + same = true; + break; + } + } + } + if (same || isBranchStart(location)) { + if (callback(previousLocation, guards, same)) { + stop = true; + break; + } + else { + continue; + } + } + visited[nodeId] = true; + } + else { + if (guards.indexOf(location) !== -1) continue; + isGuard = true; + guards.push(location); + } + if (location.previous === undefined) { + // We cannot do analysis in a catch or finally block + if (callback(undefined, guards, true)) { + stop = true; + break; + } + else { + if (!isGuard) visited[nodeId] = false; + continue; + } + } + stack.push([location, false]); + for (let i = location.previous.length - 1; i >= 0; i--) { + const item = location.previous[i]; + stack.push([item, true]); + } + } + + return stop; + + function isSameSymbol(left: Node, right: Node): boolean { + if (left === right) return false; + if (left.kind !== right.kind) return false; + if (left.kind === SyntaxKind.Identifier) { + const leftSymbol = resolveName(left, (left).text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); + const rightSymbol = resolveName(right, (right).text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); + return leftSymbol !== undefined && rightSymbol !== undefined && leftSymbol.id === rightSymbol.id; + } + if (left.kind === SyntaxKind.ThisKeyword) { + return getThisContainer(left, false) === getThisContainer(right, false); + } + if (left.kind === SyntaxKind.PropertyAccessExpression) { + if ((left).name.text !== (right).name.text) { + return false; + } + return isSameSymbol((left).expression, (right).expression); + } + return false; + } + function isBranchStart(node: Node) { + const guard = guards[guards.length - 1]; + if (!guard) return false; + return guard.node === node; + } + } + // Get the narrowed type of a given symbol at a given location - function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (!(type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) { - return type; + function getNarrowedTypeOfReference(initialType: Type, reference: Node) { + const isUnion = (initialType.flags & TypeFlags.Union) !== 0; + + if (!(initialType.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) { + return initialType; } const leftmostNode = getLeftmostIdentifierOrThis(reference); if (!leftmostNode) { - return type; + return initialType; } let top: Node; + const isDottedName = leftmostNode !== reference; + let thisKeyword: Node; + let leftmostSymbol: Symbol; if (leftmostNode.kind === SyntaxKind.Identifier) { - const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode)); + leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode)); if (!leftmostSymbol) { - return type; + return initialType; } const declaration = leftmostSymbol.valueDeclaration; if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) { - return type; + return initialType; } top = getDeclarationContainer(declaration); + + // Only narrow when symbol is variable of type any or an object, union, or type parameter type + if (!(leftmostSymbol.flags & SymbolFlags.Variable)) return initialType; + + if (!leftmostSymbol.declarations) return initialType; + for (const declaration of leftmostSymbol.declarations) { + if (getSourceFileOfNode(declaration) !== getSourceFileOfNode(reference)) return initialType; + } } - 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; - } - break; + else { + // leftmostNode is `this` keyword + if (!isDottedName) { + thisKeyword = reference; } } - let nodes: { node: Node, child: Node }; - while (nodes = nodeStack.pop()) { - const {node, child} = nodes; - 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); - } - } - break; - default: - Debug.fail("Unreachable!"); - } + const symbol = isDottedName ? undefined : leftmostSymbol; + const isThisExpression = thisKeyword !== undefined; + const isIdentifier = !isDottedName && !isThisExpression; - // Use original type if construct contains assignments to variable - if (type !== originalType && isAnyPartOfReferenceAssignedWithin(reference, node)) { - type = originalType; - } + if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; + + if (isLeftHandSideOfAssignment(reference)) { + return initialType; } - // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type - if (type === emptyUnionType) { - type = originalType; + let nodeLinks = getNodeLinks(reference); + if (nodeLinks.narrowingState === NarrowingState.Done) return nodeLinks.localType; + if (nodeLinks.narrowingState === NarrowingState.Narrowing) { + nodeLinks.narrowingState = NarrowingState.Failed; + return nodeLinks.localType = initialType; } + nodeLinks.narrowingState = NarrowingState.Narrowing; - return type; + const visited: FlowMarkerTarget[] = []; + const cache: { [nodeId: number]: Type } = {}; + + let loop = false; + let narrowedType = getType(reference, false, true); + if (nodeLinks.narrowingState === NarrowingState.Failed) { + // During recursion, the narrowing of this node failed. + return initialType; + } + if (narrowedType === emptyUnionType) narrowedType = initialType; + nodeLinks.narrowingState = NarrowingState.Done; + return nodeLinks.localType = narrowedType; + + function getType(where: Node, after: boolean, isSameSymbol: boolean) { + if (where === undefined) return initialType; + const whereId = getNodeId(where); + if (after && cache[whereId]) { + return cache[whereId]; + } + if (visited.indexOf(where) !== -1) { + loop = true; + return undefined; + } + let nodeLinks: NodeLinks; + if (isSameSymbol) { + if (after) { + const assignment = getAssignedTypeAtLocation(where); + if (assignment) { + return cache[whereId] = narrowTypeByAssignment(assignment); + } + } + nodeLinks = getNodeLinks(where); + if (nodeLinks.narrowingState === NarrowingState.Done) { + return nodeLinks.localType; + } + } + let types: Type[] = []; + visited.push(where); + const fallback = getPreviousOccurences(reference, where, handleGuards); + visited.pop(); + const type = fallback || types.length === 0 ? initialType : getUnionType(types); + if (!loop && isSameSymbol && nodeLinks.narrowingState !== NarrowingState.Failed) { + nodeLinks.localType = type; + } + if (after) { + cache[whereId] = type; + } + return type; + + function handleGuards(node: Node, guards: BranchFlow[], isSameSymbol: boolean) { + if (!node && guards.length === 0) { + return true; + } + let type = getType(node, true, isSameSymbol); + if (type === undefined) { + return false; + } + for (let i = guards.length - 1; i >= 0; i--) { + const { expression, trueBranch } = guards[i]; + const narrowed = narrowType(type, expression, trueBranch); + if (narrowed !== type && isAnyPartOfReferenceAssignedWithin(reference, expression)) { + // A variable could be reassigned in a type guard. Making this work would require some non-trivial + // work. Instead, we fall back to the initial type, since no one would probably write such code. + // Example: + // let x: string | number | boolean; + // if (typeof x === "string" || x = 42) { ... } + type = initialType; + } + else { + type = narrowed; + } + } + if (type === initialType) { + return true; + } + if (type !== emptyUnionType) { + types.push(type); + } + return false; + } + } function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { return strictNullChecks && assumeTrue && isMatchingReference(expr, reference) ? getNonNullableType(type) : type; @@ -7623,6 +7781,129 @@ namespace ts { } return type; } + function getAssignedTypeAtLocation(node: Node): Type { + if (isThisExpression) return undefined; + + // Only union types can be narrowed by assignments. + // Other types will always fall back to their initial type. + + const { parent } = node; + if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { + if ((parent).initializer && isUnion) { + if (!(parent).type) { + // The type of the variable was infered by this initializer. + // Thus, we don't have to check the type of the initializer again. + return unknownType; + } + else { + return checkExpressionCached((parent).initializer); + } + } + else { + // This catches these constructs: + // - let x: T + // - for (let y in z) {} + // - for (let y of z) {} + // - Other cases where `initialType` is not a union type + return unknownType; + } + } + if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { + // x = y + if (!isUnion) return unknownType; + const type = checkExpressionCached((parent).right); + const parentType = getAssignedTypeAtLocation(parent); + if (parentType) { + // { z: x = y } = q + return getUnionType([type, parentType]); + } + return type; + } + if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { + // for (x in z) {} + if (!isUnion) return unknownType; + return globalStringType; + } + if (parent.kind === SyntaxKind.ForOfStatement && (parent).initializer === node) { + // for (x of z) {} + if (!isUnion) return unknownType; + return checkRightHandSideOfForOf((parent).expression, false); + } + + if (parent.kind === SyntaxKind.PropertyAssignment && (parent).initializer === node) { + // {z: x} = y; + const type = getAssignedTypeAtLocation(parent.parent); + if (!type) return undefined; + return getPropertyAssignmentType(type, parent) || unknownType; + } + if (parent.kind === SyntaxKind.ShorthandPropertyAssignment && (parent).name === node) { + // {x} = y; + const type = getAssignedTypeAtLocation(parent.parent); + if (!type) return undefined; + if (!isUnion) return unknownType; + const property = getPropertyAssignmentType(type, parent) || unknownType; + if ((parent).objectAssignmentInitializer) { + const initializer = checkExpressionCached((parent).objectAssignmentInitializer); + return getUnionType([property, initializer]); + } + return property; + } + + if (parent.kind === SyntaxKind.ArrayLiteralExpression) { + // [x] = y; + const type = getAssignedTypeAtLocation(parent); + if (!type) return undefined; + const index = (parent).elements.indexOf(node); + const property = getPropertyOfType(type, index.toString()); + if (property) { + return getTypeOfSymbol(property) || unknownType; + } + const elementType = getElementTypeOfIterable(type, undefined) || checkElementTypeOfArrayOrString(type, undefined); + return elementType || unknownType; + } + if (parent.kind === SyntaxKind.SpreadElementExpression) { + if (parent.parent.kind === SyntaxKind.ArrayLiteralExpression) { + // [...x] = y; + const type = getAssignedTypeAtLocation(parent.parent); + if (!type) return undefined; + if (!isUnion) return unknownType; + + const elementType = getElementTypeOfIterator(type, undefined) || checkElementTypeOfArrayOrString(type, undefined); + if (!elementType) return unknownType; + return createArrayType(elementType); + } + } + return undefined; + } + function getPropertyAssignmentType(parentType: Type, propertyAssignment: PropertyAssignment | ShorthandPropertyAssignment) { + if (!isUnion) return unknownType; + const name = propertyAssignment.name; + if (name.kind === SyntaxKind.Identifier) { + const property = getPropertyOfType(parentType, (name).text); + if (property) return getTypeOfSymbol(property); + } + return unknownType; + } + function narrowTypeByAssignment(assignedType: Type) { + if (!isUnion) return initialType; + if (assignedType === anyType || assignedType === unknownType) return initialType; + + const assignedTypes = (assignedType.flags & TypeFlags.Union) ? ( assignedType).types : [assignedType]; + + const constituentTypes = ( initialType).types; + const assignableTypes: Type[] = []; + for (const constituentType of constituentTypes) { + for (const type of assignedTypes) { + if (isTypeAssignableTo(type, constituentType)) { + assignableTypes.push(constituentType); + } + } + } + if (assignableTypes.length === 0) { + return initialType; + } + return getUnionType(assignableTypes); + } } function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { @@ -7646,7 +7927,35 @@ namespace ts { // 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)); + const narrowedType = getNarrowedTypeOfReference(type, createTransientIdentifier(symbol, location)); + cleanTransientIdentifier(); + return narrowedType; + } + + function isLeftHandSideOfAssignment(node: Node): boolean { + const parent = node.parent; + if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node) { + return isAssignmentOperator((parent).operatorToken.kind); + } + else if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { + return hasInitializer(parent); + } + else if (parent.kind === SyntaxKind.ShorthandPropertyAssignment && (parent).name === node) { + return isLeftHandSideOfAssignment(parent.parent); + } + else if (parent.kind === SyntaxKind.PropertyAssignment && (parent).initializer === node) { + return isLeftHandSideOfAssignment(parent.parent); + } + else if (parent.kind === SyntaxKind.ArrayLiteralExpression) { + return isLeftHandSideOfAssignment(parent); + } + else if (parent.kind === SyntaxKind.SpreadElementExpression) { + return isLeftHandSideOfAssignment(parent); + } + else if (parent.kind === SyntaxKind.ForInStatement || parent.kind === SyntaxKind.ForOfStatement) { + return (parent).initializer === node; + } + return false; } function skipParenthesizedNodes(expression: Expression): Expression { @@ -8761,7 +9070,7 @@ namespace ts { // So the fact that contextualMapper is passed is not important, because the operand of a spread // element is not contextually typed. const arrayOrIterableType = checkExpressionCached(node.expression, contextualMapper); - return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false); + return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false, true); } function hasDefaultValue(node: BindingElement | Expression): boolean { @@ -11710,7 +12019,7 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false) || unknownType; + const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false, true) || unknownType; const elements = node.elements; for (let i = 0; i < elements.length; i++) { const e = elements[i]; @@ -11970,8 +12279,9 @@ namespace ts { Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant_or_a_read_only_property); // Use default messages if (ok) { + let leftOriginalType = leftType; // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported - checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined); + checkTypeAssignableTo(valueType, leftOriginalType, left, /*headMessage*/ undefined); } } } @@ -14024,7 +14334,7 @@ namespace ts { } else { const varExpr = node.initializer; - const iteratedType = checkRightHandSideOfForOf(node.expression); + const iteratedType = checkRightHandSideOfForOf(node.expression, true); // There may be a destructuring assignment on the left side if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { @@ -14107,12 +14417,12 @@ namespace ts { } } - function checkRightHandSideOfForOf(rhsExpression: Expression): Type { + function checkRightHandSideOfForOf(rhsExpression: Expression, reportError: boolean): Type { const expressionType = checkNonNullExpression(rhsExpression); - return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true); + return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, reportError); } - function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type { + function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, reportError: boolean): Type { if (isTypeAny(inputType)) { return inputType; } @@ -14132,7 +14442,7 @@ namespace ts { } } - error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + if (reportError) error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); return unknownType; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b7c011631ee18..3cd93f8667816 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); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 546d1631945cc..85e6a0e38ee68 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -443,7 +443,8 @@ namespace ts { decorators?: NodeArray; // Array of decorators (in document order) modifiers?: ModifiersArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) - parent?: Node; // Parent node (initialized by binding + parent?: Node; // Parent node (initialized by binding) + previous?: FlowMarkerTarget[]; // Previous occurencies of flowmarkers (initialized by binding) /* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files. /* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding) /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) @@ -459,6 +460,16 @@ namespace ts { flags: number; } + export type FlowMarkerTarget = Node | BranchFlow; + export type BranchFlowNode = IfStatement | WhileStatement | ForStatement | DoStatement | ConditionalExpression | BinaryExpression + export interface BranchFlow { + id: number; + previous: FlowMarkerTarget[]; + node: BranchFlowNode; + expression: Expression; + trueBranch: boolean; + } + // @kind(SyntaxKind.AbstractKeyword) // @kind(SyntaxKind.AsyncKeyword) // @kind(SyntaxKind.ConstKeyword) @@ -471,6 +482,13 @@ namespace ts { // @kind(SyntaxKind.StaticKeyword) export interface Modifier extends Node { } + /* @internal */ + export enum NarrowingState { + Uninitialized, + Narrowing, + Done, + Failed + } // @kind(SyntaxKind.Identifier) export interface Identifier extends PrimaryExpression { text: string; // Text of identifier (with escapes converted to characters) @@ -2095,6 +2113,8 @@ namespace ts { 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. superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing + narrowingState?: NarrowingState; + localType?: Type; } export const enum TypeFlags { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2966af3cb76f3..7d6cbda9624e5 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): name is (Identifier & { __weakTypeGuard: void; }) | StringLiteral | LiteralExpression { if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) { return false; } diff --git a/tests/baselines/reference/TypeGuardWithEnumUnion.types b/tests/baselines/reference/TypeGuardWithEnumUnion.types index 453ec220f021a..1cdf214b565fd 100644 --- a/tests/baselines/reference/TypeGuardWithEnumUnion.types +++ b/tests/baselines/reference/TypeGuardWithEnumUnion.types @@ -77,7 +77,7 @@ function f2(x: Color | string | string[]) { if (typeof x === "string") { >typeof x === "string" : boolean >typeof x : string ->x : Color | string | string[] +>x : string | string[] | Color >"string" : string var a = x; @@ -89,11 +89,11 @@ function f2(x: Color | string | string[]) { } else { var b = x; ->b : Color | string[] ->x : Color | string[] +>b : string[] | Color +>x : string[] | Color var b: Color | string[]; ->b : Color | string[] +>b : string[] | Color >Color : Color } } 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 +} + 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..cceeee461eb76 --- /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 : number | string + +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 : number | string + 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..8a409a3189981 --- /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 : number | string + +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 : number | string + 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..0b67def291ee7 --- /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 | RegExp | boolean +} +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..a62598f67056b --- /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 : boolean | RegExp | string | number + diff --git a/tests/baselines/reference/controlFlowForOfStatement.js b/tests/baselines/reference/controlFlowForOfStatement.js new file mode 100644 index 0000000000000..8bd17e930df42 --- /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; // number | 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; // number | boolean +} diff --git a/tests/baselines/reference/controlFlowForOfStatement.symbols b/tests/baselines/reference/controlFlowForOfStatement.symbols new file mode 100644 index 0000000000000..86058470189b6 --- /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; // number | 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..e0139da1bd5e3 --- /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; // number | boolean +>x : boolean | number +} + 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..c79330146ec96 --- /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 : number | string +} + 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..5e2a6720667d8 --- /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 : number | string + +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..f341a56bdfdf5 --- /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 : number | string | RegExp | boolean +} +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/narrowTypeByInstanceof.types b/tests/baselines/reference/narrowTypeByInstanceof.types index 8bc13d12ca11c..e6a3466b88f5c 100644 --- a/tests/baselines/reference/narrowTypeByInstanceof.types +++ b/tests/baselines/reference/narrowTypeByInstanceof.types @@ -63,7 +63,7 @@ if (elementA instanceof FileMatch && elementB instanceof FileMatch) { } else if (elementA instanceof Match && elementB instanceof Match) { >elementA instanceof Match && elementB instanceof Match : boolean >elementA instanceof Match : boolean ->elementA : Match | FileMatch +>elementA : FileMatch | Match >Match : typeof Match >elementB instanceof Match : boolean >elementB : FileMatch | Match 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/stringLiteralTypesInUnionTypes01.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types index e5ff509822b99..ee8c0f1a0a607 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes01.types @@ -14,49 +14,49 @@ var y: T = "bar"; if (x === "foo") { >x === "foo" : boolean ->x : "foo" | "bar" | "baz" +>x : "foo" >"foo" : string let a = x; ->a : "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" +>a : "foo" +>x : "foo" } else if (x !== "bar") { >x !== "bar" : boolean ->x : "foo" | "bar" | "baz" +>x : "foo" >"bar" : string let b = x || y; ->b : "foo" | "bar" | "baz" ->x || y : "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" ->y : "foo" | "bar" | "baz" +>b : "foo" | "bar" +>x || y : "foo" | "bar" +>x : "foo" +>y : "bar" } else { let c = x; ->c : "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" +>c : "foo" +>x : "foo" let d = y; ->d : "foo" | "bar" | "baz" ->y : "foo" | "bar" | "baz" +>d : "bar" +>y : "bar" let e: (typeof x) | (typeof y) = c || d; ->e : "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" ->y : "foo" | "bar" | "baz" ->c || d : "foo" | "bar" | "baz" ->c : "foo" | "bar" | "baz" ->d : "foo" | "bar" | "baz" +>e : "foo" | "bar" +>x : "foo" +>y : "bar" +>c || d : "foo" | "bar" +>c : "foo" +>d : "bar" } x = y; ->x = y : "foo" | "bar" | "baz" +>x = y : "bar" >x : "foo" | "bar" | "baz" ->y : "foo" | "bar" | "baz" +>y : "bar" y = x; ->y = x : "foo" | "bar" | "baz" +>y = x : "bar" >y : "foo" | "bar" | "baz" ->x : "foo" | "bar" | "baz" +>x : "bar" diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types index b468c620376ba..edd8aeef061b6 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes02.types @@ -14,49 +14,49 @@ var y: T = "bar"; 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.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types index 5fca6e69be996..d580861d2fba9 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes03.types @@ -29,7 +29,7 @@ else if (x !== "bar") { >b : "foo" | "bar" | number >x || y : "foo" | "bar" | number >x : "foo" | "bar" | number ->y : number | "foo" | "bar" +>y : "bar" } else { let c = x; @@ -37,25 +37,25 @@ else { >x : "foo" | "bar" | number let d = y; ->d : number | "foo" | "bar" ->y : number | "foo" | "bar" +>d : "bar" +>y : "bar" let e: (typeof x) | (typeof y) = c || d; >e : "foo" | "bar" | number >x : "foo" | "bar" | number ->y : number | "foo" | "bar" +>y : "bar" >c || d : "foo" | "bar" | number >c : "foo" | "bar" | number ->d : number | "foo" | "bar" +>d : "bar" } x = y; ->x = y : number | "foo" | "bar" +>x = y : "bar" >x : "foo" | "bar" | number ->y : number | "foo" | "bar" +>y : "bar" y = x; ->y = x : "foo" | "bar" | number +>y = x : "bar" >y : number | "foo" | "bar" ->x : "foo" | "bar" | number +>x : "bar" diff --git a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types index ad92dc360d0a9..26179a971eadf 100644 --- a/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types +++ b/tests/baselines/reference/stringLiteralTypesInUnionTypes04.types @@ -15,78 +15,78 @@ let y: T = "foo"; if (x === "") { >x === "" : boolean ->x : "" | "foo" +>x : "" >"" : string let a = x; ->a : "" | "foo" ->x : "" | "foo" +>a : "" +>x : "" } if (x !== "") { >x !== "" : boolean ->x : "" | "foo" +>x : "" >"" : string let b = x; ->b : "" | "foo" ->x : "" | "foo" +>b : "" +>x : "" } if (x == "") { >x == "" : boolean ->x : "" | "foo" +>x : "" >"" : string let c = x; ->c : "" | "foo" ->x : "" | "foo" +>c : "" +>x : "" } if (x != "") { >x != "" : boolean ->x : "" | "foo" +>x : "" >"" : string let d = x; ->d : "" | "foo" ->x : "" | "foo" +>d : "" +>x : "" } if (x) { ->x : "" | "foo" +>x : "" let e = x; ->e : "" | "foo" ->x : "" | "foo" +>e : "" +>x : "" } if (!x) { >!x : boolean ->x : "" | "foo" +>x : "" let f = x; ->f : "" | "foo" ->x : "" | "foo" +>f : "" +>x : "" } if (!!x) { >!!x : boolean >!x : boolean ->x : "" | "foo" +>x : "" let g = x; ->g : "" | "foo" ->x : "" | "foo" +>g : "" +>x : "" } if (!!!x) { >!!!x : boolean >!!x : boolean >!x : boolean ->x : "" | "foo" +>x : "" let h = x; ->h : "" | "foo" ->x : "" | "foo" +>h : "" +>x : "" } diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.js b/tests/baselines/reference/stringLiteralTypesTypePredicates01.js index fd4129d272d05..2345e8633fa19 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.js +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.js @@ -9,6 +9,9 @@ function kindIs(kind: Kind, is: Kind): boolean { } var x: Kind = "A"; +var y = x; + +x = undefined; if (kindIs(x, "A")) { let a = x; @@ -29,6 +32,8 @@ function kindIs(kind, is) { return kind === is; } var x = "A"; +var y = x; +x = undefined; if (kindIs(x, "A")) { var a = x; } @@ -48,3 +53,4 @@ declare type Kind = "A" | "B"; declare function kindIs(kind: Kind, is: "A"): kind is "A"; declare function kindIs(kind: Kind, is: "B"): kind is "B"; declare var x: Kind; +declare var y: "A"; diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols b/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols index 6b18cab743233..6faa1c393658e 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.symbols @@ -33,17 +33,25 @@ var x: Kind = "A"; >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) >Kind : Symbol(Kind, Decl(stringLiteralTypesTypePredicates01.ts, 0, 0)) +var y = x; +>y : Symbol(y, Decl(stringLiteralTypesTypePredicates01.ts, 10, 3)) +>x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) + +x = undefined; +>x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) +>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)) >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) let a = x; ->a : Symbol(a, Decl(stringLiteralTypesTypePredicates01.ts, 12, 7)) +>a : Symbol(a, Decl(stringLiteralTypesTypePredicates01.ts, 15, 7)) >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) } else { let b = x; ->b : Symbol(b, Decl(stringLiteralTypesTypePredicates01.ts, 15, 7)) +>b : Symbol(b, Decl(stringLiteralTypesTypePredicates01.ts, 18, 7)) >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) } @@ -52,11 +60,11 @@ if (!kindIs(x, "B")) { >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) let c = x; ->c : Symbol(c, Decl(stringLiteralTypesTypePredicates01.ts, 19, 7)) +>c : Symbol(c, Decl(stringLiteralTypesTypePredicates01.ts, 22, 7)) >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) } else { let d = x; ->d : Symbol(d, Decl(stringLiteralTypesTypePredicates01.ts, 22, 7)) +>d : Symbol(d, Decl(stringLiteralTypesTypePredicates01.ts, 25, 7)) >x : Symbol(x, Decl(stringLiteralTypesTypePredicates01.ts, 9, 3)) } diff --git a/tests/baselines/reference/stringLiteralTypesTypePredicates01.types b/tests/baselines/reference/stringLiteralTypesTypePredicates01.types index 41da80afd3048..7008db73db3b2 100644 --- a/tests/baselines/reference/stringLiteralTypesTypePredicates01.types +++ b/tests/baselines/reference/stringLiteralTypesTypePredicates01.types @@ -35,6 +35,15 @@ var x: Kind = "A"; >Kind : "A" | "B" >"A" : "A" +var y = x; +>y : "A" +>x : "A" + +x = undefined; +>x = undefined : undefined +>x : "A" | "B" +>undefined : undefined + if (kindIs(x, "A")) { >kindIs(x, "A") : boolean >kindIs : { (kind: "A" | "B", is: "A"): kind is "A"; (kind: "A" | "B", is: "B"): kind is "B"; } @@ -55,7 +64,7 @@ if (!kindIs(x, "B")) { >!kindIs(x, "B") : boolean >kindIs(x, "B") : boolean >kindIs : { (kind: "A" | "B", is: "A"): kind is "A"; (kind: "A" | "B", is: "B"): kind is "B"; } ->x : "A" | "B" +>x : "B" | "A" >"B" : "B" let c = x; diff --git a/tests/baselines/reference/typeArgumentsWithStringLiteralTypes01.errors.txt b/tests/baselines/reference/typeArgumentsWithStringLiteralTypes01.errors.txt index 48c7efb37e8c1..13a611281de26 100644 --- a/tests/baselines/reference/typeArgumentsWithStringLiteralTypes01.errors.txt +++ b/tests/baselines/reference/typeArgumentsWithStringLiteralTypes01.errors.txt @@ -24,11 +24,9 @@ tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes0 tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes01.ts(97,5): error TS2322: Type 'string' is not assignable to type '"Hello" | "World"'. tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes01.ts(100,25): error TS2345: Argument of type '"Hello" | "World"' is not assignable to parameter of type '"Hello"'. Type '"World"' is not assignable to type '"Hello"'. -tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes01.ts(104,25): error TS2345: Argument of type '"Hello" | "World"' is not assignable to parameter of type '"Hello"'. - Type '"World"' is not assignable to type '"Hello"'. -==== tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes01.ts (24 errors) ==== +==== tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes01.ts (23 errors) ==== declare function randBool(): boolean; declare function takeReturnString(str: string): string; @@ -182,9 +180,6 @@ tests/cases/conformance/types/stringLiteral/typeArgumentsWithStringLiteralTypes0 c = takeReturnHello(c); d = takeReturnHello(d); e = takeReturnHello(e); - ~ -!!! error TS2345: Argument of type '"Hello" | "World"' is not assignable to parameter of type '"Hello"'. -!!! error TS2345: Type '"World"' is not assignable to type '"Hello"'. // Both should be valid. a = takeReturnHelloWorld(a); diff --git a/tests/baselines/reference/typeGuardEnums.types b/tests/baselines/reference/typeGuardEnums.types index 1d39a81d78acd..25d0917a998b5 100644 --- a/tests/baselines/reference/typeGuardEnums.types +++ b/tests/baselines/reference/typeGuardEnums.types @@ -17,7 +17,7 @@ if (typeof x === "number") { >"number" : string x; // number|E|V ->x : number | E | V +>x : number } else { x; // string @@ -27,7 +27,7 @@ else { if (typeof x !== "number") { >typeof x !== "number" : boolean >typeof x : string ->x : number | string | E | V +>x : string | number >"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/typeGuardInClass.errors.txt b/tests/baselines/reference/typeGuardInClass.errors.txt deleted file mode 100644 index aa86067576f5c..0000000000000 --- a/tests/baselines/reference/typeGuardInClass.errors.txt +++ /dev/null @@ -1,30 +0,0 @@ -tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts(6,17): error TS2322: Type 'string | number' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. -tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts(13,17): error TS2322: Type 'string | number' is not assignable to type 'number'. - Type 'string' is not assignable to type 'number'. - - -==== tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts (2 errors) ==== - let x: string | number; - - if (typeof x === "string") { - let n = class { - constructor() { - let y: string = x; - ~ -!!! error TS2322: Type 'string | number' is not assignable to type 'string'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. - } - } - } - else { - let m = class { - constructor() { - let y: number = x; - ~ -!!! error TS2322: Type 'string | number' is not assignable to type 'number'. -!!! error TS2322: Type 'string' is not assignable to type 'number'. - } - } - } - \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardInClass.symbols b/tests/baselines/reference/typeGuardInClass.symbols new file mode 100644 index 0000000000000..cc0e745e4de9e --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.symbols @@ -0,0 +1,29 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts === +let x: string | number; +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + +if (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + + let n = class { +>n : Symbol(n, Decl(typeGuardInClass.ts, 3, 7)) + + constructor() { + let y: string = x; +>y : Symbol(y, Decl(typeGuardInClass.ts, 5, 15)) +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + } + } +} +else { + let m = class { +>m : Symbol(m, Decl(typeGuardInClass.ts, 10, 7)) + + constructor() { + let y: number = x; +>y : Symbol(y, Decl(typeGuardInClass.ts, 12, 15)) +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + } + } +} + diff --git a/tests/baselines/reference/typeGuardInClass.types b/tests/baselines/reference/typeGuardInClass.types new file mode 100644 index 0000000000000..93fe9f28c5e9e --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.types @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts === +let x: string | number; +>x : string | number + +if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + let n = class { +>n : typeof (Anonymous class) +>class { constructor() { let y: string = x; } } : typeof (Anonymous class) + + constructor() { + let y: string = x; +>y : string +>x : string + } + } +} +else { + let m = class { +>m : typeof (Anonymous class) +>class { constructor() { let y: number = x; } } : typeof (Anonymous class) + + constructor() { + let y: number = x; +>y : number +>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/typeGuardOfFormExpr1OrExpr2.types b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.types index acd929a7ca118..cc40f30f56952 100644 --- a/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.types +++ b/tests/baselines/reference/typeGuardOfFormExpr1OrExpr2.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 @@ -109,7 +109,7 @@ if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "numbe 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 @@ -117,9 +117,9 @@ if (typeof strOrNumOrBoolOrC === "string" || typeof strOrNumOrBoolOrC === "numbe } else { 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 diff --git a/tests/baselines/reference/typeGuardOfFormNotExpr.types b/tests/baselines/reference/typeGuardOfFormNotExpr.types index a99db08efab5b..019c6b2558ff9 100644 --- a/tests/baselines/reference/typeGuardOfFormNotExpr.types +++ b/tests/baselines/reference/typeGuardOfFormNotExpr.types @@ -100,11 +100,11 @@ if (!(typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number")) >typeof strOrNumOrBool !== "string" && typeof strOrNumOrBool !== "number" : boolean >typeof strOrNumOrBool !== "string" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : boolean | string | number >"string" : string >typeof strOrNumOrBool !== "number" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : number | boolean +>strOrNumOrBool : boolean | number >"number" : string strOrNum = strOrNumOrBool; // string | number @@ -125,13 +125,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 bool = strOrNumOrBool; // boolean diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types index 9d9f28548be75..00ca080f1d8c5 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfBoolean.types @@ -188,7 +188,7 @@ else { if (typeof boolOrC !== "boolean") { >typeof boolOrC !== "boolean" : boolean >typeof boolOrC : string ->boolOrC : boolean | C +>boolOrC : C | boolean >"boolean" : string c = boolOrC; // C diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types index d3caef24efded..e56b0b021e864 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfNumber.types @@ -152,7 +152,7 @@ else { if (typeof numOrBool !== "number") { >typeof numOrBool !== "number" : boolean >typeof numOrBool : string ->numOrBool : number | boolean +>numOrBool : boolean | number >"number" : string var x: number | boolean = numOrBool; // number | boolean @@ -168,7 +168,7 @@ else { if (typeof strOrNumOrBool !== "number") { >typeof strOrNumOrBool !== "number" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : string | boolean | number >"number" : string strOrBool = strOrNumOrBool; // string | boolean @@ -185,7 +185,7 @@ else { if (typeof numOrC !== "number") { >typeof numOrC !== "number" : boolean >typeof numOrC : string ->numOrC : number | C +>numOrC : C | number >"number" : string c = numOrC; // C diff --git a/tests/baselines/reference/typeGuardOfFormTypeOfString.types b/tests/baselines/reference/typeGuardOfFormTypeOfString.types index d6a382261ac55..9affdc839d36e 100644 --- a/tests/baselines/reference/typeGuardOfFormTypeOfString.types +++ b/tests/baselines/reference/typeGuardOfFormTypeOfString.types @@ -136,7 +136,7 @@ else { if (typeof strOrNum !== "string") { >typeof strOrNum !== "string" : boolean >typeof strOrNum : string ->strOrNum : string | number +>strOrNum : number | string >"string" : string num === strOrNum; // number @@ -153,7 +153,7 @@ else { if (typeof strOrBool !== "string") { >typeof strOrBool !== "string" : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >"string" : string bool = strOrBool; // boolean @@ -170,7 +170,7 @@ else { if (typeof strOrNumOrBool !== "string") { >typeof strOrNumOrBool !== "string" : boolean >typeof strOrNumOrBool : string ->strOrNumOrBool : string | number | boolean +>strOrNumOrBool : number | boolean | string >"string" : string numOrBool = strOrNumOrBool; // number | boolean @@ -187,7 +187,7 @@ else { if (typeof strOrC !== "string") { >typeof strOrC !== "string" : boolean >typeof strOrC : string ->strOrC : string | C +>strOrC : C | string >"string" : string c = strOrC; // C 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/typeGuardsDefeat.errors.txt b/tests/baselines/reference/typeGuardsDefeat.errors.txt deleted file mode 100644 index d4006711d45c2..0000000000000 --- a/tests/baselines/reference/typeGuardsDefeat.errors.txt +++ /dev/null @@ -1,52 +0,0 @@ -tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts(21,20): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts(21,24): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts(32,23): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts(32,27): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. - - -==== tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts (4 errors) ==== - // Also note that it is possible to defeat a type guard by calling a function that changes the - // type of the guarded variable. - function foo(x: number | string) { - function f() { - x = 10; - } - if (typeof x === "string") { - f(); - return x.length; // string - } - else { - return x++; // number - } - } - function foo2(x: number | string) { - if (typeof x === "string") { - return x.length; // string - } - else { - var f = function () { - return x * x; - ~ -!!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. - ~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. - }; - } - x = "hello"; - f(); - } - function foo3(x: number | string) { - if (typeof x === "string") { - return x.length; // string - } - else { - var f = () => x * x; - ~ -!!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. - ~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. - } - x = "hello"; - f(); - } - \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsDefeat.symbols b/tests/baselines/reference/typeGuardsDefeat.symbols new file mode 100644 index 0000000000000..388b69b578904 --- /dev/null +++ b/tests/baselines/reference/typeGuardsDefeat.symbols @@ -0,0 +1,82 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts === +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x: number | string) { +>foo : Symbol(foo, Decl(typeGuardsDefeat.ts, 0, 0)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 2, 13)) + + function f() { +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 2, 34)) + + x = 10; +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 2, 13)) + } + if (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 2, 13)) + + f(); +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 2, 34)) + + return x.length; // string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 2, 13)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } + else { + return x++; // number +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 2, 13)) + } +} +function foo2(x: number | string) { +>foo2 : Symbol(foo2, Decl(typeGuardsDefeat.ts, 13, 1)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) + + return x.length; // string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } + else { + var f = function () { +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 19, 11)) + + return x * x; +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) + + }; + } + x = "hello"; +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 14, 14)) + + f(); +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 19, 11)) +} +function foo3(x: number | string) { +>foo3 : Symbol(foo3, Decl(typeGuardsDefeat.ts, 25, 1)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) + + return x.length; // string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } + else { + var f = () => x * x; +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 31, 11)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) + } + x = "hello"; +>x : Symbol(x, Decl(typeGuardsDefeat.ts, 26, 14)) + + f(); +>f : Symbol(f, Decl(typeGuardsDefeat.ts, 31, 11)) +} + diff --git a/tests/baselines/reference/typeGuardsDefeat.types b/tests/baselines/reference/typeGuardsDefeat.types new file mode 100644 index 0000000000000..cc655d3ce0f36 --- /dev/null +++ b/tests/baselines/reference/typeGuardsDefeat.types @@ -0,0 +1,105 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsDefeat.ts === +// Also note that it is possible to defeat a type guard by calling a function that changes the +// type of the guarded variable. +function foo(x: number | string) { +>foo : (x: number | string) => number +>x : number | string + + function f() { +>f : () => void + + x = 10; +>x = 10 : number +>x : number | string +>10 : number + } + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : number | string +>"string" : string + + f(); +>f() : void +>f : () => void + + 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 +>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 { + var f = function () { +>f : () => number +>function () { return x * x; } : () => number + + return x * x; +>x * x : number +>x : number +>x : number + + }; + } + x = "hello"; +>x = "hello" : string +>x : number | string +>"hello" : string + + f(); +>f() : number +>f : () => number +} +function foo3(x: number | string) { +>foo3 : (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 { + var f = () => x * x; +>f : () => number +>() => x * x : () => number +>x * x : number +>x : number +>x : number + } + x = "hello"; +>x = "hello" : string +>x : number | string +>"hello" : string + + f(); +>f() : number +>f : () => number +} + diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js index 118ebbc02c09d..1210ebe1e59ee 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.js +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -12,36 +12,30 @@ 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 = 10 && 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 = "Hello" && x) // string + : 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); // number } 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) // string + : (x = "hello" && x); // number } function foo7(x: number | string | boolean) { return typeof x === "string" @@ -54,7 +48,7 @@ function foo8(x: number | string | boolean) { var b: number | boolean; return typeof x === "string" ? x === "hello" - : ((b = x) && // number | boolean + : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)); // number @@ -80,7 +74,7 @@ function foo11(x: number | string | boolean) { // 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 + ? x // string : ((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x @@ -91,8 +85,8 @@ function foo12(x: number | string | boolean) { // 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) // x is string, result is number + : ((b = x) // x is number | boolean && typeof x === "number" && x); // x is number } @@ -110,36 +104,30 @@ 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 = 10 && 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 = "Hello" && x) // string + : 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); // number } function foo6(x) { // Modify in both branches return typeof x === "string" - ? (x = 10 && x) // string | number - : (x = "hello" && x); // string | number + ? (x = 10 && x) // string + : (x = "hello" && x); // number } function foo7(x) { return typeof x === "string" @@ -178,7 +166,7 @@ function foo11(x) { // 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 + ? x // string : ((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x @@ -189,8 +177,8 @@ function foo12(x) { // 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) // x is string, result is 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..5db3f468dccb6 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.symbols +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.symbols @@ -25,227 +25,221 @@ 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 = 10 && 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 = "Hello" && x) // string +>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); // number +>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) // string +>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); // number +>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 : 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 : 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 = x) && // number | boolean +>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 : 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 : 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 : 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, 73, 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 : Symbol(b, Decl(typeGuardsInConditionalExpression.ts, 73, 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 : 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, 80, 1)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 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, 84, 7)) return typeof x === "string" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 15)) - ? (x = 10 && x.toString().length) // number | boolean | string - changed here ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) + ? (x = 10 && x.toString().length) // x is string, result is number +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 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(String.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 15)) +>toString : Symbol(String.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, 84, 7)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 15)) && typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 15)) && x); // x is number ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 81, 15)) } diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.types b/tests/baselines/reference/typeGuardsInConditionalExpression.types index 493a8c0a31afd..456b7a510456c 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.types +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -27,126 +27,120 @@ 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 = 10 && 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 = 10 && x) // string +>(x = 10 && x) : string +>x = 10 && x : string >x : number | string ->10 && x : number | string +>10 && x : string >10 : number ->x : number | 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) => 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 = "Hello" && x) // string : x : string | 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 = "Hello" && x) // string +>(x = "Hello" && x) : string +>x = "Hello" && x : string >x : number | string ->"Hello" && x : number | string +>"Hello" && x : string >"Hello" : string ->x : number | string +>x : string - : 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 : number | string ->10 && x : number | string +>10 && x : number >10 : number ->x : number | string +>x : number } function foo5(x: number | string) { ->foo5 : (x: number | string) => number | string +>foo5 : (x: number | string) => string | number >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 | number >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); // number +>(x = "hello" && x) : number +>x = "hello" && x : number >x : number | string ->"hello" && x : number | string +>"hello" && x : number >"hello" : string ->x : number | string +>x : number } function foo6(x: number | string) { ->foo6 : (x: number | string) => number | string +>foo6 : (x: number | string) => string | number >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) // string : (x = "hello" && 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 = 10 && x) // string +>(x = 10 && x) : string +>x = 10 && x : string >x : number | string ->10 && x : number | string +>10 && x : string >10 : number ->x : number | string +>x : string - : (x = "hello" && x); // string | number ->(x = "hello" && x) : number | string ->x = "hello" && x : number | string + : (x = "hello" && x); // number +>(x = "hello" && x) : number +>x = "hello" && x : number >x : number | string ->"hello" && x : number | string +>"hello" && x : number >"hello" : string ->x : number | string +>x : number } function foo7(x: number | string | boolean) { >foo7 : (x: number | string | boolean) => boolean @@ -187,7 +181,7 @@ function foo8(x: number | string | boolean) { >b : number | boolean return typeof x === "string" ->typeof x === "string" ? x === "hello" : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean +>typeof x === "string" ? x === "hello" : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean >typeof x === "string" : boolean >typeof x : string >x : number | string | boolean @@ -198,9 +192,9 @@ function foo8(x: number | string | boolean) { >x : string >"hello" : string - : ((b = x) && // number | boolean ->((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean ->(b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10) : boolean + : ((b = x) && // number | boolean +>((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)) : boolean +>(b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10) : boolean >(b = x) : number | boolean >b = x : number | boolean >b : number | boolean @@ -296,7 +290,7 @@ 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 | boolean >x : number | string | boolean // Mixing typeguards @@ -305,29 +299,29 @@ function foo11(x: number | string | boolean) { >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 | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x && x) : string | number | boolean >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) : 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 @@ -349,13 +343,13 @@ function foo12(x: number | string | boolean) { >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) // x is string, result is 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) // x is string, result is number >(x = 10 && x.toString().length) : number >x = 10 && x.toString().length : number >x : number | string | boolean @@ -363,24 +357,24 @@ function foo12(x: number | string | boolean) { >10 : number >x.toString().length : number >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : string +>toString : () => 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..fae2e0cdd1830 --- /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 : string | number +} + diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types index 67d1816cfc373..cd5aef3d56bee 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types @@ -21,8 +21,8 @@ function foo(x: number | string | boolean) { >f : () => string var b = x; // number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string @@ -66,8 +66,8 @@ function foo2(x: number | string | boolean) { >a : number | boolean var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string @@ -111,8 +111,8 @@ function foo3(x: number | string | boolean) { >() => { var b = x; // new scope - number | boolean return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number } : () => string var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string @@ -156,8 +156,8 @@ function foo4(x: number | string | boolean) { >a : number | boolean var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string @@ -200,8 +200,8 @@ function foo5(x: number | string | boolean) { >foo : () => void var z = x; // string ->z : number | string | boolean ->x : number | string | boolean +>z : string +>x : string } } } diff --git a/tests/baselines/reference/typeGuardsInIfStatement.js b/tests/baselines/reference/typeGuardsInIfStatement.js index de05a7bf82af3..a6ae56392caf0 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.js +++ b/tests/baselines/reference/typeGuardsInIfStatement.js @@ -1,10 +1,9 @@ //// [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. +// An assignment removes this narrowed type, and narrows the type to the type of the assigned value. function foo(x: number | string) { if (typeof x === "string") { return x.length; // string @@ -14,54 +13,54 @@ function foo(x: number | string) { } } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch 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 + // x is assigned in the if true branch if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x.length; // 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 + // false branch updates the variable if (typeof x === "string") { - return x; // string | number + return x.length; // string | number } 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 + // false branch updates the variable 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.length; // string } } function foo7(x: number | string | boolean) { @@ -125,11 +124,11 @@ function foo11(x: number | string | boolean) { return typeof x === "number" ? ( // change value of x - x = 10 && x.toString() // number | boolean | string + x = 10 && x.toString() // number ) : ( // do not change value - y = x && x.toString() // number | boolean | string + y = x && x.toString() // boolean | string ); } } @@ -137,7 +136,7 @@ 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 + return x.toString(); // string } else { x = 10; @@ -150,11 +149,10 @@ 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. +// An assignment removes this narrowed type, and narrows the type to the type of the assigned value. function foo(x) { if (typeof x === "string") { return x.length; // string @@ -164,54 +162,54 @@ function foo(x) { } } function foo2(x) { - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch 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 + // x is assigned in the if true branch if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x.length; // string } else { - return x; // string | number + return x; // number } } function foo4(x) { - // false branch updates the variable - so here it is not number + // false branch updates the variable if (typeof x === "string") { - return x; // string | number + return x.length; // string | number } 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 + // false branch updates the variable 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.length; // string } } function foo7(x) { @@ -275,11 +273,11 @@ function foo11(x) { return typeof x === "number" ? ( // change value of x - x = 10 && x.toString() // number | boolean | string + x = 10 && x.toString() // number ) : ( // do not change value - y = x && x.toString() // number | boolean | string + y = x && x.toString() // boolean | string ); } } @@ -287,7 +285,7 @@ function foo12(x) { // 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 + return x.toString(); // string } else { x = 10; diff --git a/tests/baselines/reference/typeGuardsInIfStatement.symbols b/tests/baselines/reference/typeGuardsInIfStatement.symbols index 54a65f0693b47..53e9fb5126f0b 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.symbols +++ b/tests/baselines/reference/typeGuardsInIfStatement.symbols @@ -1,304 +1,309 @@ === 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. +// 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. +// An assignment removes this narrowed type, and narrows the type to the type of the assigned value. function foo(x: number | string) { >foo : Symbol(foo, Decl(typeGuardsInIfStatement.ts, 0, 0)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 5, 13)) if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 5, 13)) return x.length; // string >x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 5, 13)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) } else { return x++; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 6, 13)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 5, 13)) } } function foo2(x: number | string) { ->foo2 : Symbol(foo2, Decl(typeGuardsInIfStatement.ts, 13, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) +>foo2 : Symbol(foo2, Decl(typeGuardsInIfStatement.ts, 12, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 13, 14)) - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 13, 14)) x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 13, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) + return x; // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 13, 14)) } else { - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 14, 14)) + return x; // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 13, 14)) } } function foo3(x: number | string) { ->foo3 : Symbol(foo3, Decl(typeGuardsInIfStatement.ts, 23, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) +>foo3 : Symbol(foo3, Decl(typeGuardsInIfStatement.ts, 22, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 23, 14)) - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 23, 14)) - x = "Hello"; // even though assigned using same type as narrowed expression ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) + x = "Hello"; +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 23, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) + return x.length; // string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 23, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) } else { - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 24, 14)) + return x; // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 23, 14)) } } function foo4(x: number | string) { ->foo4 : Symbol(foo4, Decl(typeGuardsInIfStatement.ts, 33, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) +>foo4 : Symbol(foo4, Decl(typeGuardsInIfStatement.ts, 32, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 33, 14)) - // false branch updates the variable - so here it is not number + // false branch updates the variable if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 33, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) + return x.length; // string | number +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 33, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) } else { - x = 10; // even though assigned number - this should result in x to be string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) + x = 10; +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 33, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 34, 14)) + return x; // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 33, 14)) } } function foo5(x: number | string) { ->foo5 : Symbol(foo5, Decl(typeGuardsInIfStatement.ts, 43, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) +>foo5 : Symbol(foo5, Decl(typeGuardsInIfStatement.ts, 42, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 43, 14)) - // false branch updates the variable - so here it is not number + // false branch updates the variable if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 43, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) + return x; // string +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 43, 14)) } else { x = "hello"; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 43, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 44, 14)) + return x; // string +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 43, 14)) } } function foo6(x: number | string) { ->foo6 : Symbol(foo6, Decl(typeGuardsInIfStatement.ts, 53, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) +>foo6 : Symbol(foo6, Decl(typeGuardsInIfStatement.ts, 52, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) // Modify in both branches if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) + return x; // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) } else { x = "hello"; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) - return x; // string | number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 54, 14)) + return x.length; // string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 53, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) } } function foo7(x: number | string | boolean) { ->foo7 : Symbol(foo7, Decl(typeGuardsInIfStatement.ts, 64, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>foo7 : Symbol(foo7, Decl(typeGuardsInIfStatement.ts, 63, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) } else if (typeof x === "boolean") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) return x; // boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) } else { return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 65, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 64, 14)) } } function foo8(x: number | string | boolean) { ->foo8 : Symbol(foo8, Decl(typeGuardsInIfStatement.ts, 75, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>foo8 : Symbol(foo8, Decl(typeGuardsInIfStatement.ts, 74, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 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)) +>b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 80, 11)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) if (typeof x === "boolean") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) return x; // boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) } else { return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 76, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 75, 14)) } } } function foo9(x: number | string) { ->foo9 : Symbol(foo9, Decl(typeGuardsInIfStatement.ts, 89, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) +>foo9 : Symbol(foo9, Decl(typeGuardsInIfStatement.ts, 88, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 89, 14)) var y = 10; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 91, 7)) +>y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 90, 7)) if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 89, 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)) +>y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 90, 7)) >x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 89, 14)) >length : Symbol(String.length, Decl(lib.d.ts, --, --)) return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 89, 14)) } else { return x == 10; // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 90, 14)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 89, 14)) } } function foo10(x: number | string | boolean) { ->foo10 : Symbol(foo10, Decl(typeGuardsInIfStatement.ts, 100, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>foo10 : Symbol(foo10, Decl(typeGuardsInIfStatement.ts, 99, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) // Mixing typeguard narrowing in if statement with conditional expression typeguard if (typeof x === "string") { ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) return x === "hello"; // string ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) } else { var y: boolean | string; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 107, 11)) +>y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 106, 11)) var b = x; // number | boolean ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 108, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 107, 11)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) ? x === 10 // number ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) : x; // x should be boolean ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 101, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 100, 15)) } } function foo11(x: number | string | boolean) { ->foo11 : Symbol(foo11, Decl(typeGuardsInIfStatement.ts, 113, 1)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) +>foo11 : Symbol(foo11, Decl(typeGuardsInIfStatement.ts, 112, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 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)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) return x; // string | number | boolean - x changed in else branch ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) } else { var y: number| boolean | string; ->y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 121, 11)) +>y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 120, 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)) +>b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 121, 11)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 114, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 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, --, --)) + x = 10 && x.toString() // number +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) +>toString : Symbol(Number.toString, 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, --, --)) + y = x && x.toString() // boolean | string +>y : Symbol(y, Decl(typeGuardsInIfStatement.ts, 120, 11)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) +>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) +>toString : Symbol(toString, 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)) +>foo12 : Symbol(foo12, Decl(typeGuardsInIfStatement.ts, 132, 1)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 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)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 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, --, --)) + return x.toString(); // string +>x.toString : Symbol(String.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) +>toString : Symbol(String.toString, Decl(lib.d.ts, --, --)) } else { x = 10; ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) var b = x; // number | boolean | string ->b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 142, 11)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) +>b : Symbol(b, Decl(typeGuardsInIfStatement.ts, 141, 11)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) return typeof x === "number" ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) ? x.toString() // number >x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 134, 15)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 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)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 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 index 0095a7cc768ea..8992e71f53c44 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.types +++ b/tests/baselines/reference/typeGuardsInIfStatement.types @@ -1,10 +1,9 @@ === 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. +// 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. +// An assignment removes this narrowed type, and narrows the type to the type of the assigned value. function foo(x: number | string) { >foo : (x: number | string) => number >x : number | string @@ -27,10 +26,10 @@ function foo(x: number | string) { } } function foo2(x: number | string) { ->foo2 : (x: number | string) => number | string +>foo2 : (x: number | string) => number >x : number | string - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch if (typeof x === "string") { >typeof x === "string" : boolean >typeof x : string @@ -42,75 +41,79 @@ function foo2(x: number | string) { >x : number | string >10 : number - return x; // string | number ->x : number | string + return x; // number +>x : number } else { - return x; // string | number ->x : number | string + return 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 true branch, the type is not narrowed + // x is assigned in the if true branch 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"; >x = "Hello" : string >x : number | string >"Hello" : string - return x; // string | number ->x : number | string + return x.length; // string +>x.length : number +>x : string +>length : number } else { - return x; // string | number ->x : number | string + return x; // number +>x : number } } function foo4(x: number | string) { ->foo4 : (x: number | string) => number | string +>foo4 : (x: number | string) => number >x : number | string - // false branch updates the variable - so here it is not number + // false branch updates the variable if (typeof x === "string") { >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - return x; // string | number ->x : number | string + return x.length; // string | number +>x.length : number +>x : string +>length : number } else { - x = 10; // even though assigned number - this should result in x to be string | number + x = 10; >x = 10 : number >x : number | string >10 : number - return x; // string | number ->x : number | string + return x; // number +>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 + // false branch updates the variable if (typeof x === "string") { >typeof x === "string" : boolean >typeof x : string >x : number | string >"string" : string - return x; // string | number ->x : number | string + return x; // string +>x : string } else { x = "hello"; @@ -118,12 +121,12 @@ function foo5(x: number | string) { >x : number | string >"hello" : string - return x; // string | number ->x : number | string + return x; // string +>x : string } } function foo6(x: number | string) { ->foo6 : (x: number | string) => number | string +>foo6 : (x: number | string) => number >x : number | string // Modify in both branches @@ -138,8 +141,8 @@ function foo6(x: number | string) { >x : number | string >10 : number - return x; // string | number ->x : number | string + return x; // number +>x : number } else { x = "hello"; @@ -147,8 +150,10 @@ function foo6(x: number | string) { >x : number | string >"hello" : string - return x; // string | number ->x : number | string + return x.length; // string +>x.length : number +>x : string +>length : number } } function foo7(x: number | string | boolean) { @@ -178,7 +183,7 @@ function foo7(x: number | string | boolean) { else { return x == 10; // number >x == 10 : boolean ->x : number +>x : number | string >10 : number } } @@ -214,7 +219,7 @@ function foo8(x: number | string | boolean) { else { return x == 10; // number >x == 10 : boolean ->x : number +>x : number | string >10 : number } } @@ -254,7 +259,7 @@ function foo9(x: number | string) { } } function foo10(x: number | string | boolean) { ->foo10 : (x: number | string | boolean) => boolean +>foo10 : (x: number | string | boolean) => boolean | string >x : number | string | boolean // Mixing typeguard narrowing in if statement with conditional expression typeguard @@ -278,10 +283,10 @@ function foo10(x: number | string | boolean) { >x : number | boolean return typeof x === "number" ->typeof x === "number" ? x === 10 // number : x : boolean +>typeof x === "number" ? x === 10 // number : x : boolean | string >typeof x === "number" : boolean >typeof x : string ->x : number | boolean +>x : number | string | boolean >"number" : string ? x === 10 // number @@ -290,11 +295,11 @@ function foo10(x: number | string | boolean) { >10 : number : x; // x should be boolean ->x : boolean +>x : string | boolean } } function foo11(x: number | string | boolean) { ->foo11 : (x: number | string | boolean) => number | string | boolean +>foo11 : (x: number | string | boolean) => string >x : number | string | boolean // Mixing typeguard narrowing in if statement with conditional expression typeguard @@ -306,51 +311,51 @@ function foo11(x: number | string | boolean) { >"string" : string return x; // string | number | boolean - x changed in else branch ->x : number | string | boolean +>x : string } 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 +>b : number | boolean +>x : number | 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" ? ( // change value of x x = 10 && x.toString() // number ) : ( // do not change value y = x && x.toString() // 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 ) : string // change value of x - x = 10 && x.toString() // number | boolean | string + x = 10 && x.toString() // number >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 +>x : number >toString : (radix?: number) => string ) : ( ->( // do not change value y = x && x.toString() // number | boolean | string ) : string +>( // do not change value y = x && x.toString() // boolean | string ) : string // do not change value - y = x && x.toString() // number | boolean | string + y = x && x.toString() // boolean | string >y = x && x.toString() : string >y : number | boolean | string >x && x.toString() : string ->x : number | string | boolean +>x : string | boolean >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : string | boolean +>toString : () => string ); } @@ -367,11 +372,11 @@ function foo12(x: number | string | boolean) { >x : number | string | boolean >"string" : string - return x.toString(); // string | number | boolean - x changed in else branch + return x.toString(); // string >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : string +>toString : () => string } else { x = 10; @@ -380,8 +385,8 @@ function foo12(x: number | string | boolean) { >10 : number var b = x; // number | boolean | string ->b : number | string | boolean ->x : number | string | boolean +>b : number +>x : number return typeof x === "number" >typeof x === "number" ? x.toString() // number : x.toString() : string diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols index 21e3dd3701f44..734668a9c043f 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.symbols @@ -106,17 +106,17 @@ function foo7(x: number | string | boolean) { // 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.toString : Symbol(Number.toString, 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, --, --)) +>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.toString : Symbol(Object.toString, 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, --, --)) +>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) } function foo8(x: number | string) { >foo8 : Symbol(foo8, Decl(typeGuardsInRightOperandOfAndAndOperator.ts, 45, 1)) diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types index 22d390618c165..2f85a17fca25f 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types @@ -19,42 +19,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 @@ -159,17 +159,17 @@ function foo7(x: number | string | boolean) { && ((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 >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" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string // change value of x @@ -181,7 +181,7 @@ function foo7(x: number | string | boolean) { >10 : number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string | boolean +>x : number >toString : (radix?: number) => string // do not change value @@ -190,11 +190,11 @@ function foo7(x: number | string | boolean) { >y = x && x.toString() : string >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 diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols index de9b4396d3d4a..61b6edaba8a40 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.symbols @@ -106,17 +106,17 @@ function foo7(x: number | string | boolean) { // change value of x ? (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 : 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)) diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types index 38d9a723d3a86..c96fccab3758c 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 @@ -157,19 +157,19 @@ function foo7(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) // 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 | boolean | 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())) : 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" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"number" : string // change value of x @@ -181,7 +181,7 @@ function foo7(x: number | string | boolean) { >10 : number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string | boolean +>x : number >toString : (radix?: number) => string // do not change value @@ -190,11 +190,11 @@ function foo7(x: number | string | boolean) { >y = x && x.toString() : string >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 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..69ec05be44df0 --- /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 : string | number +} + diff --git a/tests/baselines/reference/unionTypesAssignability.errors.txt b/tests/baselines/reference/unionTypesAssignability.errors.txt index cad61f41d1dbf..93cd7ca04c88f 100644 --- a/tests/baselines/reference/unionTypesAssignability.errors.txt +++ b/tests/baselines/reference/unionTypesAssignability.errors.txt @@ -22,15 +22,12 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTyp tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(37,1): error TS2322: Type 'E' is not assignable to type 'D'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(41,1): error TS2322: Type 'number' is not assignable to type 'string'. 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'. - 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'. - Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(66,5): error TS2322: Type 'U' is not assignable to type 'T'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(67,5): error TS2322: Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts(71,5): error TS2322: Type 'U' is not assignable to type 'T'. -==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts (19 errors) ==== +==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts (18 errors) ==== var unionNumberString: number | string; class C { } class D extends C { foo1() { } } @@ -133,22 +130,20 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTyp // type parameters function foo(t: T, u: U) { - t = u; // error - ~ + let tt: T; + let uu: U; + tt = u; // error + ~~ !!! error TS2322: Type 'U' is not assignable to type 'T'. - u = t; // error - ~ + uu = t; // error + ~~ !!! error TS2322: Type 'T' is not assignable to type 'U'. var x : T | U; x = t; // ok x = u; // ok - t = x; // error U not assignable to T - ~ -!!! error TS2322: Type 'T | U' is not assignable to type 'T'. -!!! error TS2322: Type 'U' is not assignable to type 'T'. - u = x; // error T not assignable to U - ~ -!!! error TS2322: Type 'T | U' is not assignable to type 'U'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. + tt = x; // error U not assignable to T + ~~ +!!! error TS2322: Type 'U' is not assignable to type 'T'. + uu = x; // error T not assignable to U } \ No newline at end of file diff --git a/tests/baselines/reference/unionTypesAssignability.js b/tests/baselines/reference/unionTypesAssignability.js index e07901347a58e..12522e348a906 100644 --- a/tests/baselines/reference/unionTypesAssignability.js +++ b/tests/baselines/reference/unionTypesAssignability.js @@ -62,13 +62,15 @@ unionNumberString = undefined; // type parameters function foo(t: T, u: U) { - t = u; // error - u = t; // error + let tt: T; + let uu: U; + tt = u; // error + uu = 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 + tt = x; // error U not assignable to T + uu = x; // error T not assignable to U } @@ -152,11 +154,13 @@ unionDE = undefined; unionNumberString = undefined; // type parameters function foo(t, u) { - t = u; // error - u = t; // error + var tt; + var uu; + tt = u; // error + uu = t; // error var x; x = t; // ok x = u; // ok - t = x; // error U not assignable to T - u = x; // error T not assignable to U + tt = x; // error U not assignable to T + uu = x; // error T not assignable to U } 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/typeGuardsInConditionalExpression.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts index 1633c80ab67b0..f7e2f03fb8a76 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInConditionalExpression.ts @@ -11,36 +11,30 @@ 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 = 10 && 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 = "Hello" && x) // string + : 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); // number } 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) // string + : (x = "hello" && x); // number } function foo7(x: number | string | boolean) { return typeof x === "string" @@ -53,7 +47,7 @@ function foo8(x: number | string | boolean) { var b: number | boolean; return typeof x === "string" ? x === "hello" - : ((b = x) && // number | boolean + : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)); // number @@ -79,7 +73,7 @@ function foo11(x: number | string | boolean) { // 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 + ? x // string : ((b = x) // x is number | boolean | string - because the assignment changed it && typeof x === "number" && (x = 10) // assignment to x @@ -90,8 +84,8 @@ function foo12(x: number | string | boolean) { // 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) // x is string, result is 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/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/typeGuardsInIfStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts index f72845c18a4a9..b262b0915e451 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts @@ -1,9 +1,8 @@ // 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. +// An assignment removes this narrowed type, and narrows the type to the type of the assigned value. function foo(x: number | string) { if (typeof x === "string") { return x.length; // string @@ -13,54 +12,54 @@ function foo(x: number | string) { } } function foo2(x: number | string) { - // x is assigned in the if true branch, the type is not narrowed + // x is assigned in the if true branch 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 + // x is assigned in the if true branch if (typeof x === "string") { - x = "Hello"; // even though assigned using same type as narrowed expression - return x; // string | number + x = "Hello"; + return x.length; // 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 + // false branch updates the variable if (typeof x === "string") { - return x; // string | number + return x.length; // string | number } 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 + // false branch updates the variable 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.length; // string } } function foo7(x: number | string | boolean) { @@ -124,11 +123,11 @@ function foo11(x: number | string | boolean) { return typeof x === "number" ? ( // change value of x - x = 10 && x.toString() // number | boolean | string + x = 10 && x.toString() // number ) : ( // do not change value - y = x && x.toString() // number | boolean | string + y = x && x.toString() // boolean | string ); } } @@ -136,7 +135,7 @@ 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 + return x.toString(); // string } else { x = 10; 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 +} diff --git a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts index a78918c8122be..4ffda1b340e65 100644 --- a/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts +++ b/tests/cases/conformance/types/stringLiteral/stringLiteralTypesTypePredicates01.ts @@ -9,6 +9,9 @@ function kindIs(kind: Kind, is: Kind): boolean { } var x: Kind = "A"; +var y = x; + +x = undefined; if (kindIs(x, "A")) { let a = x; diff --git a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts index 9ceee8845a58a..954b185f4a6ba 100644 --- a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts +++ b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/unionTypesAssignability.ts @@ -59,13 +59,15 @@ 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) { + let tt: T; + let uu: U; + tt = u; // error + uu = t; // error + var x : T | U; + x = t; // ok + x = u; // ok + tt = x; // error U not assignable to T + uu = x; // error T not assignable to U }