From 441c9995ee7d74c5ac48ddf7d292cad48842ab65 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 4 Dec 2015 13:02:51 +0100 Subject: [PATCH 01/49] Add flow markers to binder --- src/compiler/binder.ts | 212 ++++++++++++++++++++++++++++------------- src/compiler/types.ts | 10 +- 2 files changed, 155 insertions(+), 67 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7561783d1398b..a3366c2a3e947 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -17,14 +17,53 @@ namespace ts { Unreachable = 1 << 2, ReportedUnreachable = 1 << 3 } + interface Flow { + reachability: Reachability; + previous: FlowMarker[]; + } - 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: FlowMarker[] = []; + 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: [] + }; } + const defaultUnreachable: Flow = { + reachability: Reachability.Unreachable, + previous: [] + }; + const defaultReachable: Flow = { + reachability: Reachability.Reachable, + previous: [] + }; + const defaultUnintialized: Flow = { + reachability: Reachability.Unintialized, + previous: [] + }; export function getModuleInstanceState(node: Node): ModuleInstanceState { // A module is uninstantiated if it contains only @@ -112,8 +151,8 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; - let currentReachabilityState: Reachability; - let labelStack: Reachability[]; + let currentReachabilityState: Flow; + let labelStack: [Flow, FlowMarker][]; let labelIndexMap: Map; let implicitLabels: number[]; @@ -398,8 +437,8 @@ namespace ts { blockScopeContainer.locals = undefined; } - let savedReachabilityState: Reachability; - let savedLabelStack: Reachability[]; + let savedReachabilityState: Flow; + let savedLabelStack: [Flow, FlowMarker][]; let savedLabels: Map; let savedImplicitLabels: number[]; let savedHasExplicitReturn: boolean; @@ -422,14 +461,14 @@ namespace ts { savedImplicitLabels = implicitLabels; savedHasExplicitReturn = hasExplicitReturn; - currentReachabilityState = Reachability.Reachable; + currentReachabilityState = defaultReachable; hasExplicitReturn = false; labelStack = labelIndexMap = implicitLabels = undefined; } 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; @@ -509,76 +548,87 @@ 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); bind(n.statement); popImplicitLabel(postWhileLabel, postWhileState); + bindIterationFlowMarkerEnd(n); } function bindDoStatement(n: DoStatement): void { + bindFlowMarker(n); + const preDoState = currentReachabilityState; - const postDoLabel = pushImplicitLabel(); + const postDoLabel = pushImplicitLabel(n); bind(n.statement); - const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : preDoState; + const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : preDoState; popImplicitLabel(postDoLabel, postDoState); // bind expressions (don't affect reachability) bind(n.expression); + bindIterationFlowMarkerEnd(n); } function bindForStatement(n: ForStatement): void { - const preForState = currentReachabilityState; - const postForLabel = pushImplicitLabel(); - - // bind expressions (don't affect reachability) + // bind expressions (don't affect reachability, do affect identifier flow) bind(n.initializer); + bindFlowMarker(n); bind(n.condition); bind(n.incrementor); + const preForState = currentReachabilityState; + const postForLabel = pushImplicitLabel(n); + bind(n.statement); // for statement is considered infinite when it condition is either omitted or is true keyword // - for(..;;..) // - for(..;true;..) const isInfiniteLoop = (!n.condition || n.condition.kind === SyntaxKind.TrueKeyword); - const postForState = isInfiniteLoop ? Reachability.Unreachable : preForState; + const postForState = isInfiniteLoop ? defaultUnreachable : preForState; popImplicitLabel(postForLabel, postForState); + bindIterationFlowMarkerEnd(n); } function bindForInOrForOfStatement(n: ForInStatement | ForOfStatement): void { - const preStatementState = currentReachabilityState; - const postStatementLabel = pushImplicitLabel(); - - // bind expressions (don't affect reachability) + // bind expressions (don't affect reachability, do affect identfier flow) bind(n.initializer); bind(n.expression); + bindFlowMarker(n); + + const preStatementState = currentReachabilityState; + const postStatementLabel = pushImplicitLabel(n); + bind(n.statement); popImplicitLabel(postStatementLabel, preStatementState); + bindIterationFlowMarkerEnd(n); } 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); - bind(n.thenStatement); if (n.elseStatement) { const preElseState = currentReachabilityState; @@ -597,22 +647,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; @@ -630,18 +685,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 exaustive (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 exaustive (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); } @@ -652,17 +707,19 @@ namespace ts { for (const clause of n.clauses) { currentReachabilityState = startState; bind(clause); - if (clause.statements.length && currentReachabilityState === Reachability.Reachable && options.noFallthroughCasesInSwitch) { + if (clause.statements.length && currentReachabilityState.reachability === Reachability.Reachable && options.noFallthroughCasesInSwitch) { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } } 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); @@ -1163,7 +1220,7 @@ namespace ts { switch (node.kind) { /* Strict mode checks */ case SyntaxKind.Identifier: - return checkStrictModeIdentifier(node); + return bindIdentifier(node); case SyntaxKind.BinaryExpression: if (isInJavaScriptFile(node)) { if (isExportsPropertyAssignment(node)) { @@ -1447,37 +1504,56 @@ namespace ts { : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } + function bindIdentifier(node: Identifier) { + checkStrictModeIdentifier(node); + if (isExpression(node)) { + bindFlowMarker(node); + } + } + + function bindFlowMarker(node: FlowMarker) { + node.previous = currentReachabilityState.previous; + currentReachabilityState = { + reachability: currentReachabilityState.reachability, + previous: [node] + }; + } + function bindIterationFlowMarkerEnd(node: IterationStatement & FlowMarker) { + node.previous = [...node.previous, ...currentReachabilityState.previous]; + } + // reachability checks - function pushNamedLabel(name: Identifier): boolean { + function pushNamedLabel(name: Identifier, node: FlowMarker): boolean { initializeReachabilityStateIfNecessary(); if (hasProperty(labelIndexMap, name.text)) { return false; } - labelIndexMap[name.text] = labelStack.push(Reachability.Unintialized) - 1; + labelIndexMap[name.text] = labelStack.push([defaultUnintialized, node]) - 1; return true; } - function pushImplicitLabel(): number { + function pushImplicitLabel(node: FlowMarker): number { initializeReachabilityStateIfNecessary(); - const index = labelStack.push(Reachability.Unintialized) - 1; + const index = labelStack.push([defaultUnintialized, node]) - 1; 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 [state] = labelStack.pop(); + setCurrentStateAtLabel(state, outerState, label); } - function popImplicitLabel(implicitLabelIndex: number, outerState: Reachability): void { + function popImplicitLabel(implicitLabelIndex: number, outerState: Flow): void { if (labelStack.length !== implicitLabelIndex + 1) { Debug.assert(false, `Label stack: ${labelStack.length}, index:${implicitLabelIndex}`); } @@ -1488,11 +1564,12 @@ namespace ts { Debug.assert(false, `i: ${i}, index: ${implicitLabelIndex}`); } - setCurrentStateAtLabel(labelStack.pop(), outerState, /*name*/ undefined); + const [state] = labelStack.pop(); + setCurrentStateAtLabel(state, outerState, /*name*/ undefined); } - function setCurrentStateAtLabel(innerMergedState: Reachability, outerState: Reachability, label: Identifier): void { - if (innerMergedState === Reachability.Unintialized) { + function setCurrentStateAtLabel(innerMergedState: Flow, outerState: Flow, label: Identifier): void { + if (innerMergedState.reachability === Reachability.Unintialized) { if (label && !options.allowUnusedLabels) { file.bindDiagnostics.push(createDiagnosticForNode(label, Diagnostics.Unused_label)); } @@ -1503,7 +1580,7 @@ namespace ts { } } - 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); @@ -1512,13 +1589,17 @@ namespace ts { // break/continue used outside of loops return false; } - const stateAtLabel = labelStack[index]; - labelStack[index] = stateAtLabel === Reachability.Unintialized ? outerState : or(stateAtLabel, outerState); + const [stateAtLabel, node] = labelStack[index]; + if (isContinue && node.previous !== undefined) { + // node is iteration + node.previous = [...node.previous, ...currentReachabilityState.previous]; + } + labelStack[index] = [stateAtLabel.reachability === Reachability.Unintialized ? outerState : or(stateAtLabel, outerState), node]; 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 @@ -1531,7 +1612,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 @@ -1571,7 +1655,7 @@ namespace ts { if (labelIndexMap) { return; } - currentReachabilityState = Reachability.Reachable; + currentReachabilityState = defaultReachable; labelIndexMap = {}; labelStack = []; implicitLabels = []; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8b26ddaa6dda..3b8d283b145a3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -480,6 +480,10 @@ namespace ts { flags: number; } + export interface FlowMarker extends Node { + previous?: FlowMarker[]; + } + // @kind(SyntaxKind.AbstractKeyword) // @kind(SyntaxKind.AsyncKeyword) // @kind(SyntaxKind.ConstKeyword) @@ -493,7 +497,7 @@ namespace ts { export interface Modifier extends Node { } // @kind(SyntaxKind.Identifier) - export interface Identifier extends PrimaryExpression { + export interface Identifier extends PrimaryExpression, FlowMarker { text: string; // Text of identifier (with escapes converted to characters) originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later } @@ -1117,7 +1121,7 @@ namespace ts { elseStatement?: Statement; } - export interface IterationStatement extends Statement { + export interface IterationStatement extends Statement, FlowMarker { statement: Statement; } @@ -1198,7 +1202,7 @@ namespace ts { export type CaseOrDefaultClause = CaseClause | DefaultClause; // @kind(SyntaxKind.LabeledStatement) - export interface LabeledStatement extends Statement { + export interface LabeledStatement extends Statement, FlowMarker { label: Identifier; statement: Statement; } From 522a99c4a9aec669d9a93e506199fd7a2c3a9d12 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 4 Dec 2015 14:58:16 +0100 Subject: [PATCH 02/49] Add BranchFlow for type guards Do and for statements not implemented yet --- src/compiler/binder.ts | 41 ++++++++++++++++++++++++++++------------- src/compiler/types.ts | 11 +++++++++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a3366c2a3e947..94328c79f63fb 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -19,12 +19,12 @@ namespace ts { } interface Flow { reachability: Reachability; - previous: FlowMarker[]; + previous: FlowMarkerTarget[]; } function or(state1: Flow, state2: Flow): Flow { let reachable = false; - let previous: FlowMarker[] = []; + let previous: FlowMarkerTarget[] = []; if (state1.reachability === Reachability.Reachable) { reachable = true; previous = state1.previous; @@ -560,8 +560,13 @@ namespace ts { currentReachabilityState = preWhileState; const postWhileLabel = pushImplicitLabel(n); + bindBranchFlow(n, n.expression, true); bind(n.statement); - popImplicitLabel(postWhileLabel, postWhileState); + + currentReachabilityState = postWhileState; + bindBranchFlow(n, n.expression, false); + + popImplicitLabel(postWhileLabel, currentReachabilityState); bindIterationFlowMarkerEnd(n); } @@ -628,17 +633,15 @@ namespace ts { const ifFalseState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : currentReachabilityState; currentReachabilityState = ifTrueState; + 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 { @@ -1511,7 +1514,7 @@ namespace ts { } } - function bindFlowMarker(node: FlowMarker) { + function bindFlowMarker(node: Node & FlowMarker) { node.previous = currentReachabilityState.previous; currentReachabilityState = { reachability: currentReachabilityState.reachability, @@ -1521,6 +1524,18 @@ namespace ts { function bindIterationFlowMarkerEnd(node: IterationStatement & FlowMarker) { node.previous = [...node.previous, ...currentReachabilityState.previous]; } + function bindBranchFlow(node: BranchFlowNode, expression: Expression, trueBranch: boolean) { + const branchFlow: BranchFlow = { + previous: currentReachabilityState.previous, + node, + expression, + trueBranch + }; + currentReachabilityState = { + reachability: currentReachabilityState.reachability, + previous: [branchFlow] + }; + } // reachability checks diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3b8d283b145a3..e2c9e6651842c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -480,8 +480,15 @@ namespace ts { flags: number; } - export interface FlowMarker extends Node { - previous?: FlowMarker[]; + export type FlowMarkerTarget = (FlowMarker & Node) | BranchFlow; + export interface FlowMarker { + previous?: FlowMarkerTarget[]; + } + export type BranchFlowNode = IfStatement | WhileStatement | ForStatement | DoStatement | ConditionalExpression | BinaryExpression + export interface BranchFlow extends FlowMarker { + node: BranchFlowNode; + expression: Expression; + trueBranch: boolean; } // @kind(SyntaxKind.AbstractKeyword) From 78ca7b5c8dc75b92a021a3e45879102fcc46e4eb Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sun, 6 Dec 2015 14:44:22 +0100 Subject: [PATCH 03/49] Add caching of identifier narrowing in binder --- src/compiler/binder.ts | 79 ++++++++++++++++++++++++++++++++++-------- src/compiler/types.ts | 12 ++++++- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 94328c79f63fb..b30c55ad91e58 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -49,20 +49,20 @@ namespace ts { : Reachability.Unreachable; return { reachability, - previous: [] + previous: undefined }; } const defaultUnreachable: Flow = { reachability: Reachability.Unreachable, - previous: [] + previous: undefined }; const defaultReachable: Flow = { reachability: Reachability.Reachable, - previous: [] + previous: undefined }; const defaultUnintialized: Flow = { reachability: Reachability.Unintialized, - previous: [] + previous: undefined }; export function getModuleInstanceState(node: Node): ModuleInstanceState { @@ -161,6 +161,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; @@ -541,6 +542,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; @@ -729,6 +736,50 @@ namespace ts { } } + function bindBinaryExpression(node: BinaryExpression) { + const isAnd = node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken; + const isOr = node.operatorToken.kind === SyntaxKind.BarBarToken; + + if (isInJavaScriptFile(node)) { + if (isExportsPropertyAssignment(node)) { + bindExportsPropertyAssignment(node); + } + else if (isModuleExportsAssignment(node)) { + bindModuleExportsAssignment(node); + } + } + + if (isAnd || isOr) { + 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) { + bind(node.right); + bind(node.left); + } + else { + forEachChild(node, bind); + } + } + + function bindConditionalExpression(node: ConditionalExpression) { + 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: @@ -1171,7 +1222,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); @@ -1225,14 +1276,6 @@ namespace ts { case SyntaxKind.Identifier: return bindIdentifier(node); case SyntaxKind.BinaryExpression: - if (isInJavaScriptFile(node)) { - if (isExportsPropertyAssignment(node)) { - bindExportsPropertyAssignment(node); - } - else if (isModuleExportsAssignment(node)) { - bindModuleExportsAssignment(node); - } - } return checkStrictModeBinaryExpression(node); case SyntaxKind.CatchClause: return checkStrictModeCatchClause(node); @@ -1510,6 +1553,11 @@ namespace ts { function bindIdentifier(node: Identifier) { checkStrictModeIdentifier(node); if (isExpression(node)) { + if (node.parent.kind === SyntaxKind.PropertyAccessExpression && ( node.parent).name === node) { + return; + } + node.narrowingState = NarrowingState.Uninitialized; + node.localType = undefined; bindFlowMarker(node); } } @@ -1522,10 +1570,13 @@ namespace ts { }; } function bindIterationFlowMarkerEnd(node: IterationStatement & FlowMarker) { - node.previous = [...node.previous, ...currentReachabilityState.previous]; + if (node.previous) { + node.previous = [...node.previous, ...currentReachabilityState.previous]; + } } function bindBranchFlow(node: BranchFlowNode, expression: Expression, trueBranch: boolean) { const branchFlow: BranchFlow = { + id: branchFlowId++, previous: currentReachabilityState.previous, node, expression, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e2c9e6651842c..b80b85a196fbb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -486,6 +486,7 @@ namespace ts { } export type BranchFlowNode = IfStatement | WhileStatement | ForStatement | DoStatement | ConditionalExpression | BinaryExpression export interface BranchFlow extends FlowMarker { + id: number; node: BranchFlowNode; expression: Expression; trueBranch: boolean; @@ -503,10 +504,19 @@ 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, FlowMarker { text: string; // Text of identifier (with escapes converted to characters) originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + /* @internal */ narrowingState?: NarrowingState; + /* @internal */ localType?: Type; } // @kind(SyntaxKind.QualifiedName) @@ -1714,7 +1724,7 @@ namespace ts { } export interface TypeChecker { - getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; + getTypeOfSymbolAtLocation(symbol: Symbol, node: Identifier): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol; From 1fb2db6c000bd0dbc36b4455ec754bff8fa7ec7f Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sun, 6 Dec 2015 14:44:41 +0100 Subject: [PATCH 04/49] Implement flow based type guards in checker --- src/compiler/checker.ts | 176 +++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 67 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f20c5421285f..c7fd6a9680e0e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6468,81 +6468,105 @@ namespace ts { } } + interface PreviousOccurency { + node: Identifier; + guards: BranchFlow[]; + } + function getPreviousOccurencies(symbol: Symbol, identifier: Identifier, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { + let stop = false; + const visited: { [id: number]: boolean } = {}; + const visitedGuards: { [id: number]: BranchFlow } = {}; + + worker(identifier, [], 1); + return stop; + + function worker(location: FlowMarkerTarget, guards: BranchFlow[], level: number) { + if (stop) return; + let isGuard = false; + if (( location).kind !== undefined) { + if (visited[location.id]) return; + visited[location.id] = true; + const otherSymbol = getSymbolAtLocation( location); + if (location !== identifier && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { + stop = callback( location, guards); + return; + } + } + else { + if (visitedGuards[location.id]) return; + isGuard = true; + guards.push( location); + } + if (location.previous === undefined) { + // We cannot do analysis in a catch or finally block + stop = callback(undefined, guards); + return; + } + for (const item of location.previous) { + worker(item, guards, level + 1); + } + if (isGuard) { + guards.pop(); + } + } + } + // Get the narrowed type of a given symbol at a given location - function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { - let type = getTypeOfSymbol(symbol); + function getNarrowedTypeOfSymbol(symbol: Symbol, identifier: Identifier) { + if (identifier.localType) return identifier.localType; + + const initialType = getTypeOfSymbol(symbol); + Debug.assert(identifier.kind === SyntaxKind.Identifier, "node in getNarrowedTypeOfSymbol should be an identifier"); + // Only narrow when symbol is variable of type any or an object, union, or type parameter type - if (node && symbol.flags & SymbolFlags.Variable) { - if (isTypeAny(type) || type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { - const declaration = getDeclarationOfKind(symbol, SyntaxKind.VariableDeclaration); - const top = declaration && getDeclarationContainer(declaration); - const originalType = type; - const nodeStack: {node: Node, child: Node}[] = []; - 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: - // Stop at the first containing file or module declaration - break loop; - } - if (node === top) { - break; - } - } + if (!identifier || !(symbol.flags & SymbolFlags.Variable)) return initialType; + if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; - 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!"); - } + if (!symbol.declarations) return initialType; + for (const declaration of symbol.declarations) { + if (getSourceFile(declaration) !== getSourceFile(identifier)) return initialType; + } + const visited: FlowMarkerTarget[] = []; - // Use original type if construct contains assignments to variable - if (type !== originalType && isVariableAssignedWithin(symbol, node)) { - type = originalType; - } - } + const narrowedType = getType(identifier, false); + return identifier.localType = narrowedType; + + function getType(location: Identifier, after: boolean) { + if (location === undefined) return initialType; + if (visited.indexOf(location) !== -1) return undefined; - // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type - if (type === emptyUnionType) { - type = originalType; + if (after) { + const assignment = getAssignmentAtLocation(location); + if (assignment) return getTypeOfNode(assignment); + } + + let types: Type[] = []; + visited.push(location); + const fallback = getPreviousOccurencies(symbol, location, handleGuards); + visited.pop(); + if (fallback) { + return initialType; + } + return types.length === 0 ? initialType : getUnionType(types); + + function handleGuards(node: Identifier, guards: BranchFlow[]) { + if (!node && guards.length === 0) { + return true; + } + let type = getType(node, true); + if (type === undefined) { + type = initialType; } + for (let i = guards.length - 1; i >= 0; i--) { + const { expression, trueBranch } = guards[i]; + type = narrowType(type, expression, trueBranch); + } + if (type === initialType) return true; + types.push(type); + return false; } } - return type; - function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // Check that we have 'typeof ' on the left and string literal on the right if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) { @@ -6735,6 +6759,15 @@ namespace ts { } return type; } + function getAssignmentAtLocation(node: Identifier) { + const { parent } = node; + if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { + return (parent).initializer; + } + if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { + return (parent).right; + } + } } function checkIdentifier(node: Identifier): Type { @@ -10535,8 +10568,17 @@ namespace ts { const ok = checkReferenceExpression(left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant); // Use default messages if (ok) { + let leftOriginalType = leftType; + if (left.kind === SyntaxKind.Identifier) { + const symbol = getSymbolOfNode(left); + if (!symbol) { + leftOriginalType = anyType; + } else { + leftOriginalType = getTypeOfSymbol(symbol); + } + } // 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); } } } From f1c9a82c360dc2f254f0b9696ca4a51bdd043f3b Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 9 Dec 2015 14:58:09 +0100 Subject: [PATCH 05/49] Narrow assignments to unions only --- src/compiler/checker.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c7fd6a9680e0e..54e580e025dce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6537,7 +6537,7 @@ namespace ts { if (after) { const assignment = getAssignmentAtLocation(location); - if (assignment) return getTypeOfNode(assignment); + if (assignment) return narrowTypeByAssignment(assignment); } let types: Type[] = []; @@ -6768,6 +6768,22 @@ namespace ts { return (parent).right; } } + function narrowTypeByAssignment(expression: Expression) { + // Narrow union types only + if (!(initialType.flags & TypeFlags.Union)) return initialType; + const type = getTypeOfNode(expression); + const constituentTypes = ( initialType).types; + const assignableTypes: Type[] = []; + for (const constituentType of constituentTypes) { + if (isTypeAssignableTo(type, constituentType)) { + assignableTypes.push(constituentType); + } + } + if (assignableTypes.length === 0) { + return initialType; + } + return getUnionType(assignableTypes); + } } function checkIdentifier(node: Identifier): Type { From d3dd1fad9668e2c6e38f4e32778b874453596130 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 9 Dec 2015 14:58:23 +0100 Subject: [PATCH 06/49] Make isDeclarationName a weak type guard for Identifiers --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 66daf4cf3115e..0a8e8bf11edb2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1326,7 +1326,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; } From 702edac3382362a0f961e008d364ee517037de0d Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 9 Dec 2015 16:04:00 +0100 Subject: [PATCH 07/49] Remove trailing space --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 54e580e025dce..2537a6bd14099 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6770,7 +6770,7 @@ namespace ts { } function narrowTypeByAssignment(expression: Expression) { // Narrow union types only - if (!(initialType.flags & TypeFlags.Union)) return initialType; + if (!(initialType.flags & TypeFlags.Union)) return initialType; const type = getTypeOfNode(expression); const constituentTypes = ( initialType).types; const assignableTypes: Type[] = []; From 86700146d211f49238c9e29af1ceb5cb64b9d625 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 9 Dec 2015 16:31:05 +0100 Subject: [PATCH 08/49] Make getNarrowedTypeOfSymbol work on all nodes again Instead of only on Identifiers --- src/compiler/binder.ts | 16 +++++++------- src/compiler/checker.ts | 47 +++++++++++++++++++++++++---------------- src/compiler/types.ts | 19 ++++++++--------- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b30c55ad91e58..1a2ad0896d4c1 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -151,8 +151,8 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; - let currentReachabilityState: Flow; - let labelStack: [Flow, FlowMarker][]; + let currentReachabilityState: Flow = defaultReachable; + let labelStack: [Flow, Node][]; let labelIndexMap: Map; let implicitLabels: number[]; @@ -439,7 +439,7 @@ namespace ts { } let savedReachabilityState: Flow; - let savedLabelStack: [Flow, FlowMarker][]; + let savedLabelStack: [Flow, Node][]; let savedLabels: Map; let savedImplicitLabels: number[]; let savedHasExplicitReturn: boolean; @@ -1202,6 +1202,7 @@ namespace ts { } node.parent = parent; + node.previous = currentReachabilityState.previous; const savedInStrictMode = inStrictMode; if (!savedInStrictMode) { @@ -1562,14 +1563,13 @@ namespace ts { } } - function bindFlowMarker(node: Node & FlowMarker) { - node.previous = currentReachabilityState.previous; + function bindFlowMarker(node: Node) { currentReachabilityState = { reachability: currentReachabilityState.reachability, previous: [node] }; } - function bindIterationFlowMarkerEnd(node: IterationStatement & FlowMarker) { + function bindIterationFlowMarkerEnd(node: IterationStatement) { if (node.previous) { node.previous = [...node.previous, ...currentReachabilityState.previous]; } @@ -1590,7 +1590,7 @@ namespace ts { // reachability checks - function pushNamedLabel(name: Identifier, node: FlowMarker): boolean { + function pushNamedLabel(name: Identifier, node: Node): boolean { initializeReachabilityStateIfNecessary(); if (hasProperty(labelIndexMap, name.text)) { @@ -1600,7 +1600,7 @@ namespace ts { return true; } - function pushImplicitLabel(node: FlowMarker): number { + function pushImplicitLabel(node: Node): number { initializeReachabilityStateIfNecessary(); const index = labelStack.push([defaultUnintialized, node]) - 1; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2537a6bd14099..11a2d33cf3d48 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6472,12 +6472,12 @@ namespace ts { node: Identifier; guards: BranchFlow[]; } - function getPreviousOccurencies(symbol: Symbol, identifier: Identifier, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { + function getPreviousOccurencies(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { let stop = false; const visited: { [id: number]: boolean } = {}; const visitedGuards: { [id: number]: BranchFlow } = {}; - worker(identifier, [], 1); + worker(where, [], 1); return stop; function worker(location: FlowMarkerTarget, guards: BranchFlow[], level: number) { @@ -6487,7 +6487,7 @@ namespace ts { if (visited[location.id]) return; visited[location.id] = true; const otherSymbol = getSymbolAtLocation( location); - if (location !== identifier && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { + if (location !== where && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { stop = callback( location, guards); return; } @@ -6512,37 +6512,48 @@ namespace ts { } // Get the narrowed type of a given symbol at a given location - function getNarrowedTypeOfSymbol(symbol: Symbol, identifier: Identifier) { - if (identifier.localType) return identifier.localType; - + function getNarrowedTypeOfSymbol(symbol: Symbol, location: Node) { const initialType = getTypeOfSymbol(symbol); - Debug.assert(identifier.kind === SyntaxKind.Identifier, "node in getNarrowedTypeOfSymbol should be an identifier"); + Debug.assert(location.kind === SyntaxKind.Identifier, "node in getNarrowedTypeOfSymbol should be an identifier"); // Only narrow when symbol is variable of type any or an object, union, or type parameter type - if (!identifier || !(symbol.flags & SymbolFlags.Variable)) return initialType; + if (!location || !(symbol.flags & SymbolFlags.Variable)) return initialType; if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; + let saveLocalType = false; + if (location.kind === SyntaxKind.Identifier) { + const locationSymbol = getSymbolOfNode(location); + if (locationSymbol && locationSymbol.id === symbol.id) { + if ((location).localType) return (location).localType; + saveLocalType = true; + } + } + if (!symbol.declarations) return initialType; for (const declaration of symbol.declarations) { - if (getSourceFile(declaration) !== getSourceFile(identifier)) return initialType; + if (getSourceFile(declaration) !== getSourceFile(location)) return initialType; } const visited: FlowMarkerTarget[] = []; - const narrowedType = getType(identifier, false); - return identifier.localType = narrowedType; + const narrowedType = getType(location, false); + if (saveLocalType) { + (location).localType = narrowedType; + } + return narrowedType; - function getType(location: Identifier, after: boolean) { - if (location === undefined) return initialType; - if (visited.indexOf(location) !== -1) return undefined; + function getType(where: Node, after: boolean) { + if (where === undefined) return initialType; + if (visited.indexOf(where) !== -1) return undefined; - if (after) { - const assignment = getAssignmentAtLocation(location); + const isIdentifier = where.kind === SyntaxKind.Identifier; + if (isIdentifier && after) { + const assignment = getAssignmentAtLocation(where); if (assignment) return narrowTypeByAssignment(assignment); } let types: Type[] = []; - visited.push(location); - const fallback = getPreviousOccurencies(symbol, location, handleGuards); + visited.push(where); + const fallback = getPreviousOccurencies(symbol, where, handleGuards); visited.pop(); if (fallback) { return initialType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b80b85a196fbb..b40bb84086627 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -464,7 +464,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) @@ -480,13 +481,11 @@ namespace ts { flags: number; } - export type FlowMarkerTarget = (FlowMarker & Node) | BranchFlow; - export interface FlowMarker { - previous?: FlowMarkerTarget[]; - } + export type FlowMarkerTarget = Node | BranchFlow; export type BranchFlowNode = IfStatement | WhileStatement | ForStatement | DoStatement | ConditionalExpression | BinaryExpression - export interface BranchFlow extends FlowMarker { + export interface BranchFlow { id: number; + previous: FlowMarkerTarget[]; node: BranchFlowNode; expression: Expression; trueBranch: boolean; @@ -512,7 +511,7 @@ namespace ts { Failed } // @kind(SyntaxKind.Identifier) - export interface Identifier extends PrimaryExpression, FlowMarker { + export interface Identifier extends PrimaryExpression { text: string; // Text of identifier (with escapes converted to characters) originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later /* @internal */ narrowingState?: NarrowingState; @@ -1138,7 +1137,7 @@ namespace ts { elseStatement?: Statement; } - export interface IterationStatement extends Statement, FlowMarker { + export interface IterationStatement extends Statement { statement: Statement; } @@ -1219,7 +1218,7 @@ namespace ts { export type CaseOrDefaultClause = CaseClause | DefaultClause; // @kind(SyntaxKind.LabeledStatement) - export interface LabeledStatement extends Statement, FlowMarker { + export interface LabeledStatement extends Statement { label: Identifier; statement: Statement; } @@ -1724,7 +1723,7 @@ namespace ts { } export interface TypeChecker { - getTypeOfSymbolAtLocation(symbol: Symbol, node: Identifier): Type; + getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; getPropertyOfType(type: Type, propertyName: string): Symbol; From 905a240503a0af68858180acc30d79fb582cdef4 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 6 Feb 2016 17:30:01 +0100 Subject: [PATCH 09/49] Bind flow of loops and named labels --- src/compiler/binder.ts | 139 +++++++++++++++++++++++++++-------------- src/compiler/types.ts | 1 + 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 55c4441b003d3..1754e9fecd8bc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -21,6 +21,11 @@ namespace ts { reachability: Reachability; previous: FlowMarkerTarget[]; } + interface Label { + node: Node; + continueFlow: Flow; + breakFlow: Flow; + } function or(state1: Flow, state2: Flow): Flow { let reachable = false; @@ -152,9 +157,11 @@ namespace ts { // state used by reachability checks let hasExplicitReturn: boolean; let currentReachabilityState: Flow = defaultReachable; - let labelStack: [Flow, Node][]; + let labelStack: Label[]; + let lastImplicitLabel: [Label, Node]; let labelIndexMap: Map; let implicitLabels: number[]; + let flowIndex: number; // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or @@ -172,6 +179,7 @@ namespace ts { inStrictMode = !!file.externalModuleIndicator; classifiableNames = {}; Symbol = objectAllocator.getSymbolConstructor(); + flowIndex = 0; if (!file.locals) { bind(file); @@ -439,7 +447,7 @@ namespace ts { } let savedReachabilityState: Flow; - let savedLabelStack: [Flow, Node][]; + let savedLabelStack: Label[]; let savedLabels: Map; let savedImplicitLabels: number[]; let savedHasExplicitReturn: boolean; @@ -570,62 +578,78 @@ namespace ts { bindBranchFlow(n, n.expression, true); bind(n.statement); + const postBodyState = currentReachabilityState; + bindIterationFlowMarkerEnd(n); + currentReachabilityState = postWhileState; bindBranchFlow(n, n.expression, false); - popImplicitLabel(postWhileLabel, currentReachabilityState); - bindIterationFlowMarkerEnd(n); + popImplicitLabel(postWhileLabel, currentReachabilityState, n); } function bindDoStatement(n: DoStatement): void { bindFlowMarker(n); - const preDoState = currentReachabilityState; - const postDoLabel = pushImplicitLabel(n); bind(n.statement); - const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : preDoState; - popImplicitLabel(postDoLabel, postDoState); // bind expressions (don't affect reachability) - bind(n.expression); + bind(n.expression, true); + bindFlowMarker(n.expression); + + 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); - bindFlowMarker(n); - bind(n.condition); - bind(n.incrementor); + const postInitializer = currentReachabilityState; + const loopStartNode = n.condition || n.statement; + + bind(n.condition, loopStartNode === n.condition); + if (loopStartNode === n.condition) bindFlowMarker(loopStartNode); const preForState = currentReachabilityState; const postForLabel = pushImplicitLabel(n); - bind(n.statement); + if (n.condition) bindBranchFlow(n, n.condition, true); + bind(n.statement, loopStartNode === n.statement); + if (loopStartNode === n.statement) bindFlowMarker(loopStartNode); + + if (n.incrementor) { + bind(n.incrementor, true); + bindFlowMarker(n.incrementor); + } + 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 ? defaultUnreachable : preForState; - popImplicitLabel(postForLabel, postForState); - bindIterationFlowMarkerEnd(n); + 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.initializer); bind(n.expression); - - bindFlowMarker(n); + bind(n.initializer, true); + bindFlowMarker(n.initializer); const preStatementState = currentReachabilityState; const postStatementLabel = pushImplicitLabel(n); bind(n.statement); - popImplicitLabel(postStatementLabel, preStatementState); - bindIterationFlowMarkerEnd(n); + bindIterationFlowMarkerEnd(n.initializer); + popImplicitLabel(postStatementLabel, preStatementState, n.initializer); } function bindIfStatement(n: IfStatement): void { @@ -1196,13 +1220,17 @@ 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; + node.flowIndex = flowIndex++; + if (flowMarker) { + bindFlowMarker(node); + } const savedInStrictMode = inStrictMode; if (!savedInStrictMode) { @@ -1581,9 +1609,14 @@ namespace ts { previous: [node] }; } - function bindIterationFlowMarkerEnd(node: IterationStatement) { - if (node.previous) { - node.previous = [...node.previous, ...currentReachabilityState.previous]; + 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) { @@ -1602,20 +1635,23 @@ namespace ts { // reachability checks - function pushNamedLabel(name: Identifier, node: Node): boolean { + function pushLabel(node: Node) { + return labelStack.push({ continueFlow: defaultUnintialized, breakFlow: defaultUnintialized, node }) - 1; + } + function pushNamedLabel(name: Identifier, node: LabeledStatement): boolean { initializeReachabilityStateIfNecessary(); if (hasProperty(labelIndexMap, name.text)) { return false; } - labelIndexMap[name.text] = labelStack.push([defaultUnintialized, node]) - 1; + labelIndexMap[name.text] = pushLabel(node); return true; } function pushImplicitLabel(node: Node): number { initializeReachabilityStateIfNecessary(); - const index = labelStack.push([defaultUnintialized, node]) - 1; + const index = pushLabel(node); implicitLabels.push(index); return index; } @@ -1627,11 +1663,21 @@ namespace ts { labelIndexMap[label.text] = undefined; - const [state] = labelStack.pop(); - setCurrentStateAtLabel(state, outerState, label); + const labelState = labelStack.pop(); + if (!options.allowUnusedLabels + && labelState.breakFlow.reachability === Reachability.Unintialized + && labelState.continueFlow.reachability === Reachability.Unintialized) { + + 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: Flow): void { + function popImplicitLabel(implicitLabelIndex: number, outerState: Flow, continueToNode?: Node): void { if (labelStack.length !== implicitLabelIndex + 1) { Debug.assert(false, `Label stack: ${labelStack.length}, index:${implicitLabelIndex}`); } @@ -1642,19 +1688,19 @@ namespace ts { Debug.assert(false, `i: ${i}, index: ${implicitLabelIndex}`); } - const [state] = labelStack.pop(); - setCurrentStateAtLabel(state, outerState, /*name*/ undefined); + const labelState = labelStack.pop(); + setCurrentStateAtLabel(labelState, outerState, continueToNode); + lastImplicitLabel = [labelState, continueToNode]; } - function setCurrentStateAtLabel(innerMergedState: Flow, outerState: Flow, label: Identifier): void { - if (innerMergedState.reachability === Reachability.Unintialized) { - 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); } } @@ -1667,12 +1713,13 @@ namespace ts { // break/continue used outside of loops return false; } - const [stateAtLabel, node] = labelStack[index]; - if (isContinue && node.previous !== undefined) { + const state = labelStack[index]; + if (isContinue) { // node is iteration - node.previous = [...node.previous, ...currentReachabilityState.previous]; + state.continueFlow = or(state.continueFlow, currentReachabilityState); + } else { + state.breakFlow = or(state.breakFlow, currentReachabilityState); } - labelStack[index] = [stateAtLabel.reachability === Reachability.Unintialized ? outerState : or(stateAtLabel, outerState), node]; return true; } @@ -1733,7 +1780,7 @@ namespace ts { if (labelIndexMap) { return; } - currentReachabilityState = defaultReachable; + currentReachabilityState = currentReachabilityState || defaultReachable; labelIndexMap = {}; labelStack = []; implicitLabels = []; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a8118c4a1c8e3..a7f4df9cf4591 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -470,6 +470,7 @@ namespace ts { /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding) previous?: FlowMarkerTarget[]; // Previous occurencies of flowmarkers (initialized by binding) + flowIndex?: number; // Index in which this item was visited in flow analysis in binder. /* @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) From b8c0e2cfe018331e20e359cdbf1025e6088d737a Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 6 Feb 2016 17:32:59 +0100 Subject: [PATCH 10/49] Fix flow based type guards in checker --- src/compiler/checker.ts | 107 ++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 055a8807d5e4f..de9c2ecb0c52c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2528,7 +2528,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); @@ -2565,7 +2565,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)) { @@ -6560,39 +6560,47 @@ namespace ts { function getPreviousOccurencies(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { let stop = false; const visited: { [id: number]: boolean } = {}; - const visitedGuards: { [id: number]: BranchFlow } = {}; + const guards: BranchFlow[] = []; - worker(where, [], 1); + worker(where); return stop; - function worker(location: FlowMarkerTarget, guards: BranchFlow[], level: number) { + function worker(location: FlowMarkerTarget) { if (stop) return; let isGuard = false; + let nodeFlowIndex: number; if (( location).kind !== undefined) { - if (visited[location.id]) return; - visited[location.id] = true; + nodeFlowIndex = (location).flowIndex; + if (visited[nodeFlowIndex]) return; + const otherSymbol = getSymbolAtLocation( location); if (location !== where && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { stop = callback( location, guards); return; } + visited[nodeFlowIndex] = true; } else { - if (visitedGuards[location.id]) return; + if (guards.indexOf( location) !== -1) return; isGuard = true; guards.push( location); } if (location.previous === undefined) { // We cannot do analysis in a catch or finally block stop = callback(undefined, guards); + if (!isGuard) visited[nodeFlowIndex] = false; return; } for (const item of location.previous) { - worker(item, guards, level + 1); + worker(item); } if (isGuard) { guards.pop(); } + else { + visited[nodeFlowIndex] = false; + } + return; } } @@ -6607,21 +6615,31 @@ namespace ts { let saveLocalType = false; if (location.kind === SyntaxKind.Identifier) { - const locationSymbol = getSymbolOfNode(location); + const locationSymbol = getSymbolAtLocation(location); if (locationSymbol && locationSymbol.id === symbol.id) { - if ((location).localType) return (location).localType; + if ((location).narrowingState === NarrowingState.Done) return (location).localType; + if ((location).narrowingState === NarrowingState.Narrowing) { + (location).narrowingState = NarrowingState.Failed; + return (location).localType = initialType; + } saveLocalType = true; + (location).narrowingState = NarrowingState.Narrowing; } } - if (!symbol.declarations) return initialType; for (const declaration of symbol.declarations) { if (getSourceFile(declaration) !== getSourceFile(location)) return initialType; } const visited: FlowMarkerTarget[] = []; + let previousFlowIndex = Infinity; const narrowedType = getType(location, false); if (saveLocalType) { + if ((location).narrowingState === NarrowingState.Failed) { + // During recursion, the narrowing of this node failed. + return initialType; + } + (location).narrowingState = NarrowingState.Done; (location).localType = narrowedType; } return narrowedType; @@ -6629,17 +6647,23 @@ namespace ts { function getType(where: Node, after: boolean) { if (where === undefined) return initialType; if (visited.indexOf(where) !== -1) return undefined; - const isIdentifier = where.kind === SyntaxKind.Identifier; if (isIdentifier && after) { - const assignment = getAssignmentAtLocation(where); - if (assignment) return narrowTypeByAssignment(assignment); + const assignment = getAssignedTypeAtLocation(where); + if (assignment) { + if (where.flowIndex > previousFlowIndex) { + return initialType; + } + return narrowTypeByAssignment(assignment()); + } } - + const savePreviousFlowIndex = previousFlowIndex; + if (where.flowIndex < previousFlowIndex) previousFlowIndex = where.flowIndex; let types: Type[] = []; visited.push(where); const fallback = getPreviousOccurencies(symbol, where, handleGuards); visited.pop(); + previousFlowIndex = savePreviousFlowIndex; if (fallback) { return initialType; } @@ -6657,7 +6681,9 @@ namespace ts { const { expression, trueBranch } = guards[i]; type = narrowType(type, expression, trueBranch); } - if (type === initialType) return true; + if (type === initialType) { + return true; + } types.push(type); return false; } @@ -6895,19 +6921,35 @@ namespace ts { } return type; } - function getAssignmentAtLocation(node: Identifier) { + function getAssignedTypeAtLocation(node: Identifier) { const { parent } = node; if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { - return (parent).initializer; + if ((parent).initializer) { + return () => getTypeOfExpression((parent).initializer); + } + else { + // This catches these constructs: + // - let x: string + // - for (let y in z) {} + // - for (let y of z) {} + return () => initialType; + } } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { - return (parent).right; + return () => getTypeOfExpression((parent).right); + } + if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { + // for (x in z) {} + return () => globalStringType; + } + if (parent.kind === SyntaxKind.ForOfStatement && (parent).initializer === node) { + // for (x of z) {} + return () => checkRightHandSideOfForOf((parent).expression, false); } } - function narrowTypeByAssignment(expression: Expression) { + function narrowTypeByAssignment(type: Type) { // Narrow union types only if (!(initialType.flags & TypeFlags.Union)) return initialType; - const type = getTypeOfNode(expression); const constituentTypes = ( initialType).types; const assignableTypes: Type[] = []; for (const constituentType of constituentTypes) { @@ -7664,7 +7706,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 { @@ -10514,7 +10556,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]; @@ -11191,6 +11233,7 @@ namespace ts { } else if (typePredicateNode.parameterName) { let hasReportedError = false; + const parameterName = typePredicate.parameterName; for (var param of node.parameters) { if (hasReportedError) { break; @@ -11201,11 +11244,11 @@ namespace ts { (function checkBindingPattern(pattern: BindingPattern) { for (const element of pattern.elements) { if (element.name.kind === SyntaxKind.Identifier && - (element.name).text === typePredicate.parameterName) { + (element.name).text === parameterName) { error(typePredicateNode.parameterName, Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, - typePredicate.parameterName); + parameterName); hasReportedError = true; break; } @@ -11221,7 +11264,7 @@ namespace ts { if (!hasReportedError) { error(typePredicateNode.parameterName, Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName); + parameterName); } } } @@ -12824,7 +12867,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) { @@ -12906,12 +12949,12 @@ namespace ts { } } - function checkRightHandSideOfForOf(rhsExpression: Expression): Type { + function checkRightHandSideOfForOf(rhsExpression: Expression, showError: boolean): Type { const expressionType = getTypeOfExpression(rhsExpression); - return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true); + return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, showError); } - function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type { + function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, showError: boolean): Type { if (isTypeAny(inputType)) { return inputType; } @@ -12931,7 +12974,7 @@ namespace ts { } } - error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); + if (showError) error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType)); return unknownType; } From b38547230a8d120984f845f66c4c4883a03c0ea1 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 6 Feb 2016 17:45:48 +0100 Subject: [PATCH 11/49] Fix errors after merge --- src/compiler/binder.ts | 9 --------- src/compiler/checker.ts | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 70e74822dec5b..3e4d032967ba0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -823,15 +823,6 @@ namespace ts { const isAnd = node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken; const isOr = node.operatorToken.kind === SyntaxKind.BarBarToken; - if (isInJavaScriptFile(node)) { - if (isExportsPropertyAssignment(node)) { - bindExportsPropertyAssignment(node); - } - else if (isModuleExportsAssignment(node)) { - bindModuleExportsAssignment(node); - } - } - if (isAnd || isOr) { bind(node.left); const preRightState = currentReachabilityState; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8c3600618d44b..2a6956b9c05d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6927,7 +6927,7 @@ namespace ts { } if (!symbol.declarations) return initialType; for (const declaration of symbol.declarations) { - if (getSourceFile(declaration) !== getSourceFile(location)) return initialType; + if (getSourceFileOfNode(declaration) !== getSourceFileOfNode(location)) return initialType; } const visited: FlowMarkerTarget[] = []; From 6f1d84e9b46a09a4bc8836757c5f3bc824f0d846 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 6 Feb 2016 20:28:14 +0100 Subject: [PATCH 12/49] Remove restriction on type narrowing with assignments in loops --- src/compiler/checker.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2a6956b9c05d7..568f050f93c56 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6931,7 +6931,6 @@ namespace ts { } const visited: FlowMarkerTarget[] = []; - let previousFlowIndex = Infinity; const narrowedType = getType(location, false); if (saveLocalType) { if ((location).narrowingState === NarrowingState.Failed) { @@ -6950,19 +6949,13 @@ namespace ts { if (isIdentifier && after) { const assignment = getAssignedTypeAtLocation(where); if (assignment) { - if (where.flowIndex > previousFlowIndex) { - return initialType; - } - return narrowTypeByAssignment(assignment()); + return narrowTypeByAssignment(assignment); } } - const savePreviousFlowIndex = previousFlowIndex; - if (where.flowIndex < previousFlowIndex) previousFlowIndex = where.flowIndex; let types: Type[] = []; visited.push(where); const fallback = getPreviousOccurencies(symbol, where, handleGuards); visited.pop(); - previousFlowIndex = savePreviousFlowIndex; if (fallback) { return initialType; } @@ -7224,26 +7217,26 @@ namespace ts { const { parent } = node; if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { if ((parent).initializer) { - return () => getTypeOfExpression((parent).initializer); + return getTypeOfExpression((parent).initializer); } else { // This catches these constructs: // - let x: string // - for (let y in z) {} // - for (let y of z) {} - return () => initialType; + return initialType; } } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { - return () => getTypeOfExpression((parent).right); + return getTypeOfExpression((parent).right); } if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { // for (x in z) {} - return () => globalStringType; + return globalStringType; } if (parent.kind === SyntaxKind.ForOfStatement && (parent).initializer === node) { // for (x of z) {} - return () => checkRightHandSideOfForOf((parent).expression, false); + return checkRightHandSideOfForOf((parent).expression, false); } } function narrowTypeByAssignment(type: Type) { From ffa8db3f7c1a7e8a771e2b731ee33e91016e7d60 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 6 Feb 2016 21:37:36 +0100 Subject: [PATCH 13/49] Fix assignment checking --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 568f050f93c56..c6c698b536cf5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11352,7 +11352,7 @@ namespace ts { if (ok) { let leftOriginalType = leftType; if (left.kind === SyntaxKind.Identifier) { - const symbol = getSymbolOfNode(left); + const symbol = getSymbolAtLocation(left); if (!symbol) { leftOriginalType = anyType; } else { From 844a8c132a02bf8fb5caec75e9b77aa2ca104b59 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 8 Feb 2016 10:10:34 +0100 Subject: [PATCH 14/49] Remove flowIndex, use getNodeId --- src/compiler/binder.ts | 3 --- src/compiler/checker.ts | 12 ++++++------ src/compiler/types.ts | 1 - 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3e4d032967ba0..5459137a66511 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -161,7 +161,6 @@ namespace ts { let lastImplicitLabel: [Label, Node]; let labelIndexMap: Map; let implicitLabels: number[]; - let flowIndex: number; // state used for emit helpers let hasClassExtends: boolean; @@ -186,7 +185,6 @@ namespace ts { classifiableNames = {}; Symbol = objectAllocator.getSymbolConstructor(); - flowIndex = 0; if (!file.locals) { bind(file); @@ -1283,7 +1281,6 @@ namespace ts { node.parent = parent; node.previous = currentReachabilityState.previous; - node.flowIndex = flowIndex++; if (flowMarker) { bindFlowMarker(node); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c6c698b536cf5..4039db9cc391e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6867,17 +6867,17 @@ namespace ts { function worker(location: FlowMarkerTarget) { if (stop) return; let isGuard = false; - let nodeFlowIndex: number; + let nodeId: number; if (( location).kind !== undefined) { - nodeFlowIndex = (location).flowIndex; - if (visited[nodeFlowIndex]) return; + nodeId = getNodeId(location); + if (visited[nodeId]) return; const otherSymbol = getSymbolAtLocation( location); if (location !== where && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { stop = callback( location, guards); return; } - visited[nodeFlowIndex] = true; + visited[nodeId] = true; } else { if (guards.indexOf( location) !== -1) return; @@ -6887,7 +6887,7 @@ namespace ts { if (location.previous === undefined) { // We cannot do analysis in a catch or finally block stop = callback(undefined, guards); - if (!isGuard) visited[nodeFlowIndex] = false; + if (!isGuard) visited[nodeId] = false; return; } for (const item of location.previous) { @@ -6897,7 +6897,7 @@ namespace ts { guards.pop(); } else { - visited[nodeFlowIndex] = false; + visited[nodeId] = false; } return; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c3a2714ebbed8..f61d983a57f58 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -446,7 +446,6 @@ namespace ts { /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding) previous?: FlowMarkerTarget[]; // Previous occurencies of flowmarkers (initialized by binding) - flowIndex?: number; // Index in which this item was visited in flow analysis in binder. /* @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) From ff1b2d6cdd06f850ac382050d3d2454082329fd4 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 8 Feb 2016 10:18:21 +0100 Subject: [PATCH 15/49] Remove PreviousOccurency interface --- src/compiler/checker.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4039db9cc391e..1310600c80683 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6852,10 +6852,6 @@ namespace ts { } } - interface PreviousOccurency { - node: Identifier; - guards: BranchFlow[]; - } function getPreviousOccurencies(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { let stop = false; const visited: { [id: number]: boolean } = {}; From 6c7c678bbffcff0f1287da067b8e0dbc0043ee75 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 8 Feb 2016 10:18:35 +0100 Subject: [PATCH 16/49] Fix typo --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1310600c80683..64d1338f28ad1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6852,7 +6852,7 @@ namespace ts { } } - function getPreviousOccurencies(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { + function getPreviousOccurences(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { let stop = false; const visited: { [id: number]: boolean } = {}; const guards: BranchFlow[] = []; @@ -6950,7 +6950,7 @@ namespace ts { } let types: Type[] = []; visited.push(where); - const fallback = getPreviousOccurencies(symbol, where, handleGuards); + const fallback = getPreviousOccurences(symbol, where, handleGuards); visited.pop(); if (fallback) { return initialType; From 035d973d573cdd59c540e2c92a7fa1fe7a5039bf Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 8 Feb 2016 11:20:00 +0100 Subject: [PATCH 17/49] Fix binding of condition less for statement --- src/compiler/binder.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5459137a66511..7f051b585e34d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -678,7 +678,6 @@ namespace ts { if (n.condition) bindBranchFlow(n, n.condition, true); bind(n.statement, loopStartNode === n.statement); - if (loopStartNode === n.statement) bindFlowMarker(loopStartNode); if (n.incrementor) { bind(n.incrementor, true); From b2e51a30c2cebc3daec31e2f6195d1bb1ee0e8a0 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 11 Feb 2016 12:58:35 +0100 Subject: [PATCH 18/49] Handle narrowing in var declarations --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7f051b585e34d..ef0dd294c6a27 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1750,7 +1750,7 @@ namespace ts { function bindIdentifier(node: Identifier) { checkStrictModeIdentifier(node); - if (isExpression(node)) { + if (isExpression(node) || (node.parent.kind === SyntaxKind.VariableDeclaration && (node.parent).name === node)) { if (node.parent.kind === SyntaxKind.PropertyAccessExpression && ( node.parent).name === node) { return; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 64d1338f28ad1..193a54762e7c6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6902,12 +6902,15 @@ namespace ts { // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, location: Node) { const initialType = getTypeOfSymbol(symbol); + const isUnion = (initialType.flags & TypeFlags.Union) !== 0; Debug.assert(location.kind === SyntaxKind.Identifier, "node in getNarrowedTypeOfSymbol should be an identifier"); // Only narrow when symbol is variable of type any or an object, union, or type parameter type if (!location || !(symbol.flags & SymbolFlags.Variable)) return initialType; if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; + if (location.parent.kind === SyntaxKind.VariableDeclaration && ( location.parent).name === location) return initialType; + let saveLocalType = false; if (location.kind === SyntaxKind.Identifier) { const locationSymbol = getSymbolAtLocation(location); @@ -7210,34 +7213,41 @@ namespace ts { return type; } function getAssignedTypeAtLocation(node: Identifier) { + // 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) { + if ((parent).initializer && !isUnion) { return getTypeOfExpression((parent).initializer); } else { // This catches these constructs: - // - let x: string + // - let x: T // - for (let y in z) {} // - for (let y of z) {} + // - Other cases where `initialType` is not a union type return initialType; } } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { + if (!isUnion) return initialType; return getTypeOfExpression((parent).right); } if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { // for (x in z) {} + if (!isUnion) return initialType; return globalStringType; } if (parent.kind === SyntaxKind.ForOfStatement && (parent).initializer === node) { // for (x of z) {} + if (!isUnion) return initialType; return checkRightHandSideOfForOf((parent).expression, false); } } function narrowTypeByAssignment(type: Type) { // Narrow union types only - if (!(initialType.flags & TypeFlags.Union)) return initialType; + if (!isUnion) return initialType; const constituentTypes = ( initialType).types; const assignableTypes: Type[] = []; for (const constituentType of constituentTypes) { From 2747c9827c2fd84a93c887dbf0f24dbe6be1411f Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 11 Feb 2016 13:01:07 +0100 Subject: [PATCH 19/49] Rename `showError` to `reportError` --- src/compiler/checker.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 193a54762e7c6..cdeb2fceb93cc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13484,12 +13484,12 @@ namespace ts { } } - function checkRightHandSideOfForOf(rhsExpression: Expression, showError: boolean): Type { + function checkRightHandSideOfForOf(rhsExpression: Expression, reportError: boolean): Type { const expressionType = getTypeOfExpression(rhsExpression); - return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, showError); + return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, reportError); } - function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, showError: boolean): Type { + function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, reportError: boolean): Type { if (isTypeAny(inputType)) { return inputType; } @@ -13509,7 +13509,7 @@ namespace ts { } } - if (showError) 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; } From b58eb60951c4a7f238baac81d0a895bafcc8967d Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 11 Feb 2016 16:08:35 +0100 Subject: [PATCH 20/49] Merge remote-tracking branch 'Microsoft/master' into guards # Conflicts: # src/compiler/binder.ts --- Jakefile.js | 75 ++++---- doc/handbook/README.md | 4 + doc/wiki/README.md | 6 + src/compiler/binder.ts | 12 +- src/compiler/checker.ts | 164 +++++++++--------- src/compiler/declarationEmitter.ts | 131 +++++++------- src/compiler/parser.ts | 48 ++--- src/compiler/types.ts | 18 +- src/harness/harness.ts | 5 +- src/services/services.ts | 43 +++-- tests/baselines/reference/malformedTags.js | 17 ++ .../baselines/reference/malformedTags.symbols | 13 ++ tests/baselines/reference/malformedTags.types | 13 ++ .../operatorsAndIntersectionTypes.js | 57 ++++++ .../operatorsAndIntersectionTypes.symbols | 100 +++++++++++ .../operatorsAndIntersectionTypes.types | 132 ++++++++++++++ ...BeforeEmitParameterPropertyDeclaration1.ts | 12 ++ ...oreEmitParameterPropertyDeclaration1ES6.ts | 13 ++ ...SuperCallBeforeEmitPropertyDeclaration1.ts | 15 ++ ...erCallBeforeEmitPropertyDeclaration1ES6.ts | 15 ++ ...arationAndParameterPropertyDeclaration1.ts | 13 ++ ...tionAndParameterPropertyDeclaration1ES6.ts | 14 ++ .../cases/conformance/salsa/malformedTags.ts | 10 ++ .../operatorsAndIntersectionTypes.ts | 30 ++++ .../fourslash/goToDefinitionImportedNames3.ts | 2 +- 25 files changed, 726 insertions(+), 236 deletions(-) create mode 100644 doc/handbook/README.md create mode 100644 doc/wiki/README.md create mode 100644 tests/baselines/reference/malformedTags.js create mode 100644 tests/baselines/reference/malformedTags.symbols create mode 100644 tests/baselines/reference/malformedTags.types create mode 100644 tests/baselines/reference/operatorsAndIntersectionTypes.js create mode 100644 tests/baselines/reference/operatorsAndIntersectionTypes.symbols create mode 100644 tests/baselines/reference/operatorsAndIntersectionTypes.types create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1.ts create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1ES6.ts create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts create mode 100644 tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1ES6.ts create mode 100644 tests/cases/conformance/salsa/malformedTags.ts create mode 100644 tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts diff --git a/Jakefile.js b/Jakefile.js index 7b85aacaa89c7..84248ca34d1c8 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -223,51 +223,59 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); * @param prereqs: prerequisite tasks to compiling the file * @param prefixes: a list of files to prepend to the target file * @param useBuiltCompiler: true to use the built compiler, false to use the LKG - * @param noOutFile: true to compile without using --out - * @param generateDeclarations: true to compile using --declaration - * @param outDir: true to compile using --outDir - * @param keepComments: false to compile using --removeComments + * @parap {Object} opts - property bag containing auxiliary options + * @param {boolean} opts.noOutFile: true to compile without using --out + * @param {boolean} opts.generateDeclarations: true to compile using --declaration + * @param {string} opts.outDir: value for '--outDir' command line option + * @param {boolean} opts.keepComments: false to compile using --removeComments + * @param {boolean} opts.preserveConstEnums: true if compiler should keep const enums in code + * @param {boolean} opts.noResolve: true if compiler should not include non-rooted files in compilation + * @param {boolean} opts.stripInternal: true if compiler should remove declarations marked as @internal + * @param {boolean} opts.noMapRoot: true if compiler omit mapRoot option * @param callback: a function to execute after the compilation process ends */ -function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOutFile, generateDeclarations, outDir, preserveConstEnums, keepComments, noResolve, stripInternal, callback) { +function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { file(outFile, prereqs, function() { var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; var options = "--noImplicitAny --noEmitOnError --pretty"; - + opts = opts || {}; // Keep comments when specifically requested // or when in debug mode. - if (!(keepComments || useDebugMode)) { + if (!(opts.keepComments || useDebugMode)) { options += " --removeComments"; } - if (generateDeclarations) { + if (opts.generateDeclarations) { options += " --declaration"; } - if (preserveConstEnums || useDebugMode) { + if (opts.preserveConstEnums || useDebugMode) { options += " --preserveConstEnums"; } - if (outDir) { - options += " --outDir " + outDir; + if (opts.outDir) { + options += " --outDir " + opts.outDir; } - if (!noOutFile) { + if (!opts.noOutFile) { options += " --out " + outFile; } else { options += " --module commonjs" } - if(noResolve) { + if(opts.noResolve) { options += " --noResolve"; } if (useDebugMode) { - options += " -sourcemap -mapRoot file:///" + path.resolve(path.dirname(outFile)); + options += " -sourcemap"; + if (!opts.noMapRoot) { + options += " -mapRoot file:///" + path.resolve(path.dirname(outFile)); + } } - if (stripInternal) { + if (opts.stripInternal) { options += " --stripInternal" } @@ -382,13 +390,7 @@ compileFile(/*outfile*/configureNightlyJs, /*prereqs*/ [configureNightlyTs], /*prefixes*/ [], /*useBuiltCompiler*/ false, - /*noOutFile*/ false, - /*generateDeclarations*/ false, - /*outDir*/ undefined, - /*preserveConstEnums*/ undefined, - /*keepComments*/ false, - /*noResolve*/ false, - /*stripInternal*/ false); + { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); task("setDebugMode", function() { useDebugMode = true; @@ -438,6 +440,7 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename); compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); +var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js"); var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); @@ -446,13 +449,7 @@ var nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_s compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, - /*noOutFile*/ false, - /*generateDeclarations*/ true, - /*outDir*/ undefined, - /*preserveConstEnums*/ true, - /*keepComments*/ true, - /*noResolve*/ false, - /*stripInternal*/ true, + { noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true }, /*callback*/ function () { jake.cpR(servicesFile, nodePackageFile, {silent: true}); @@ -475,6 +472,16 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); }); +compileFile(servicesFileInBrowserTest, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources), + /*prefixes*/ [copyright], + /*useBuiltCompiler*/ true, + { noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true, noMapRoot: true }, + /*callback*/ function () { + var content = fs.readFileSync(servicesFileInBrowserTest).toString(); + var i = content.lastIndexOf("\n"); + fs.writeFileSync(servicesFileInBrowserTest, content.substring(0, i) + "\r\n//# sourceURL=../built/local/typeScriptServices.js" + content.substring(i)); + }); + var serverFile = path.join(builtLocalDirectory, "tsserver.js"); compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); @@ -486,8 +493,7 @@ compileFile( [builtLocalDirectory, copyright].concat(languageServiceLibrarySources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, - /*noOutFile*/ false, - /*generateDeclarations*/ true); + { noOutFile: false, generateDeclarations: true }); // Local target to build the language service server library desc("Builds language service server library"); @@ -720,7 +726,7 @@ task("generate-code-coverage", ["tests", builtLocalDirectory], function () { // Browser tests var nodeServerOutFile = 'tests/webTestServer.js' var nodeServerInFile = 'tests/webTestServer.ts' -compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, /*noOutFile*/ true); +compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true }); desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() { @@ -729,7 +735,7 @@ task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() }, {async: true}); desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], port=, browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory], function() { +task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function() { cleanTestDirs(); host = "node" port = process.env.port || process.env.p || '8888'; @@ -881,7 +887,8 @@ var tslintRulesOutFiles = tslintRules.map(function(p) { desc("Compiles tslint rules to js"); task("build-rules", tslintRulesOutFiles); tslintRulesFiles.forEach(function(ruleFile, i) { - compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, /*noOutFile*/ true, /*generateDeclarations*/ false, path.join(builtLocalDirectory, "tslint")); + compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, + { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); }); function getLinterOptions() { diff --git a/doc/handbook/README.md b/doc/handbook/README.md new file mode 100644 index 0000000000000..2d2e0e83a466d --- /dev/null +++ b/doc/handbook/README.md @@ -0,0 +1,4 @@ +# The TypeScript Handbook + +The contents of the TypeScript Handbook can be read from [its GitHub repository](https://github.com/Microsoft/TypeScript-Handbook). +Issues and pull requests should be directed there. \ No newline at end of file diff --git a/doc/wiki/README.md b/doc/wiki/README.md new file mode 100644 index 0000000000000..199611978409f --- /dev/null +++ b/doc/wiki/README.md @@ -0,0 +1,6 @@ +# The TypeScript Wiki + +To read the wiki, [visit the wiki on GitHub](https://github.com/Microsoft/TypeScript/wiki). + +To contribute by filing an issue or sending a pull request, [visit the wiki repository](https://github.com/Microsoft/TypeScript-wiki). + diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ef0dd294c6a27..5e893f4489b56 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -12,7 +12,7 @@ namespace ts { } const enum Reachability { - Unintialized = 1 << 0, + Uninitialized = 1 << 0, Reachable = 1 << 1, Unreachable = 1 << 2, ReportedUnreachable = 1 << 3 @@ -439,7 +439,7 @@ namespace ts { // the getLocalNameOfContainer function in the type checker to validate that the local name // used for a container is unique. function bindChildren(node: Node) { - // Before we recurse into a node's chilren, we first save the existing parent, container + // Before we recurse into a node's children, we first save the existing parent, container // and block-container. Then after we pop out of processing the children, we restore // these saved values. const saveParent = parent; @@ -464,7 +464,7 @@ namespace ts { // Finally, if this is a block-container, then we clear out any existing .locals object // it may contain within it. This happens in incremental scenarios. Because we can be // reusing a node from a previous compilation, that node may have had 'locals' created - // for it. We must clear this so we don't accidently move any stale data forward from + // for it. We must clear this so we don't accidentally move any stale data forward from // a previous compilation. const containerFlags = getContainerFlags(node); if (containerFlags & ContainerFlags.IsContainer) { @@ -785,7 +785,7 @@ namespace ts { const hasDefault = forEach(n.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); - // post switch state is unreachable if switch is exaustive (has a default case) and does not have fallthrough from the last case + // 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); @@ -889,7 +889,7 @@ namespace ts { case SyntaxKind.Block: // do not treat blocks directly inside a function as a block-scoped-container. - // Locals that reside in this block should go to the function locals. Othewise 'x' + // Locals that reside in this block should go to the function locals. Otherwise 'x' // would not appear to be a redeclaration of a block scoped local in the following // example: // @@ -1079,7 +1079,7 @@ namespace ts { const identifier = prop.name; - // ECMA-262 11.1.5 Object Initialiser + // ECMA-262 11.1.5 Object Initializer // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true // a.This production is contained in strict code and IsDataDescriptor(previous) is true and // IsDataDescriptor(propId.descriptor) is true. diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cdeb2fceb93cc..37f8addc90fcd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -467,10 +467,10 @@ namespace ts { * @return a tuple of two symbols */ function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): [Symbol, Symbol] { - const constructoDeclaration = parameter.parent; + const constructorDeclaration = parameter.parent; const classDeclaration = parameter.parent.parent; - const parameterSymbol = getSymbol(constructoDeclaration.locals, parameterName, SymbolFlags.Value); + const parameterSymbol = getSymbol(constructorDeclaration.locals, parameterName, SymbolFlags.Value); const propertySymbol = getSymbol(classDeclaration.symbol.members, parameterName, SymbolFlags.Value); if (parameterSymbol && propertySymbol) { @@ -1460,7 +1460,7 @@ namespace ts { function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) { if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) { // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) - // and if symbolfrom symbolTable or alias resolution matches the symbol, + // and if symbolFromSymbolTable or alias resolution matches the symbol, // check the symbol can be qualified, it is only then this symbol is accessible return !forEach(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) && canQualifySymbol(symbolFromSymbolTable, meaning); @@ -1531,7 +1531,7 @@ namespace ts { return qualify; } - function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult { + function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult { if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) { const initialSymbol = symbol; let meaningToLook = meaning; @@ -1541,7 +1541,7 @@ namespace ts { if (accessibleSymbolChain) { const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0]); if (!hasAccessibleDeclarations) { - return { + return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning), errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined, @@ -2140,10 +2140,10 @@ namespace ts { } } - function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) { + function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { const targetSymbol = getTargetSymbol(symbol); if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface || targetSymbol.flags & SymbolFlags.TypeAlias) { - buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaraiton, flags); + buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaration, flags); } } @@ -2613,7 +2613,7 @@ namespace ts { function getJSDocTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration): JSDocType { // First, see if this node has an @type annotation on it directly. const typeTag = getJSDocTypeTag(declaration); - if (typeTag) { + if (typeTag && typeTag.typeExpression) { return typeTag.typeExpression.type; } @@ -2623,7 +2623,7 @@ namespace ts { // @type annotation might have been on the variable statement, try that instead. const annotation = getJSDocTypeTag(declaration.parent.parent); - if (annotation) { + if (annotation && annotation.typeExpression) { return annotation.typeExpression.type; } } @@ -3421,7 +3421,7 @@ namespace ts { // Returns true if the class or interface member given by the symbol is free of "this" references. The // function may return false for symbols that are actually free of "this" references because it is not // feasible to perform a complete analysis in all cases. In particular, property members with types - // inferred from their initializers and function members with inferred return types are convervatively + // inferred from their initializers and function members with inferred return types are conservatively // assumed not to be free of "this" references. function isIndependentMember(symbol: Symbol): boolean { if (symbol.declarations && symbol.declarations.length === 1) { @@ -5591,7 +5591,7 @@ namespace ts { } function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { - if (!(target.flags & TypeFlags.ObjectLiteralPatternWithComputedProperties) && someConstituentTypeHasKind(target, TypeFlags.ObjectType)) { + if (!(target.flags & TypeFlags.ObjectLiteralPatternWithComputedProperties) && maybeTypeOfKind(target, TypeFlags.ObjectType)) { for (const prop of getPropertiesOfObjectType(source)) { if (!isKnownProperty(target, prop.name)) { if (reportErrors) { @@ -5892,7 +5892,7 @@ namespace ts { if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length && isAbstractConstructorType(source) && !isAbstractConstructorType(target)) { // An abstract constructor type is not assignable to a non-abstract constructor type - // as it would otherwise be possible to new an abstract class. Note that the assignablity + // as it would otherwise be possible to new an abstract class. Note that the assignability // check we perform for an extends clause excludes construct signatures from the target, // so this check never proceeds. if (reportErrors) { @@ -6579,7 +6579,7 @@ namespace ts { } } else if (source.flags & TypeFlags.UnionOrIntersection) { - // Source is a union or intersection type, infer from each consituent type + // Source is a union or intersection type, infer from each constituent type const sourceTypes = (source).types; for (const sourceType of sourceTypes) { inferFromTypes(sourceType, target); @@ -7498,7 +7498,7 @@ namespace ts { if (container.kind === SyntaxKind.FunctionExpression) { if (getSpecialPropertyAssignmentKind(container.parent) === SpecialPropertyAssignmentKind.PrototypeProperty) { // Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container') - const className = (((container.parent as BinaryExpression) // x.protoype.y = f + const className = (((container.parent as BinaryExpression) // x.prototype.y = f .left as PropertyAccessExpression) // x.prototype.y .expression as PropertyAccessExpression) // x.prototype .expression; // x @@ -7515,7 +7515,7 @@ namespace ts { function getTypeForThisExpressionFromJSDoc(node: Node) { const typeTag = getJSDocTypeTag(node); - if (typeTag && typeTag.typeExpression.type.kind === SyntaxKind.JSDocFunctionType) { + if (typeTag && typeTag.typeExpression && typeTag.typeExpression.type && typeTag.typeExpression.type.kind === SyntaxKind.JSDocFunctionType) { const jsDocFunctionType = typeTag.typeExpression.type; if (jsDocFunctionType.parameters.length > 0 && jsDocFunctionType.parameters[0].type.kind === SyntaxKind.JSDocThisType) { return getTypeFromTypeNode(jsDocFunctionType.parameters[0].type); @@ -8021,7 +8021,7 @@ namespace ts { * Otherwise this may not be very useful. * * In cases where you *are* working on this function, you should understand - * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContetxualType'. + * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'. * * - Use 'getContextualType' when you are simply going to propagate the result to the expression. * - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type. @@ -8279,7 +8279,7 @@ namespace ts { } function isTypeAnyOrAllConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean { - return isTypeAny(type) || allConstituentTypesHaveKind(type, kind); + return isTypeAny(type) || isTypeOfKind(type, kind); } function isNumericLiteralName(name: string) { @@ -8688,10 +8688,10 @@ namespace ts { } /// e.g. "props" for React.d.ts, - /// or 'undefined' if ElementAttributesPropery doesn't exist (which means all + /// or 'undefined' if ElementAttributesProperty doesn't exist (which means all /// non-intrinsic elements' attributes type is 'any'), /// or '' if it has 0 properties (which means every - /// non-instrinsic elements' attributes type is the element instance type) + /// non-intrinsic elements' attributes type is the element instance type) function getJsxElementPropertiesName() { // JSX const jsxNamespace = getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/undefined); @@ -8699,7 +8699,7 @@ namespace ts { const attribsPropTypeSym = jsxNamespace && getSymbol(jsxNamespace.exports, JsxNames.ElementAttributesPropertyNameContainer, SymbolFlags.Type); // JSX.ElementAttributesProperty [type] const attribPropType = attribsPropTypeSym && getDeclaredTypeOfSymbol(attribsPropTypeSym); - // The properites of JSX.ElementAttributesProperty + // The properties of JSX.ElementAttributesProperty const attribProperties = attribPropType && getPropertiesOfType(attribPropType); if (attribProperties) { @@ -9530,7 +9530,7 @@ namespace ts { // Tagged template expressions will always have `undefined` for `excludeArgument[0]`. if (excludeArgument) { for (let i = 0; i < argCount; i++) { - // No need to check for omitted args and template expressions, their exlusion value is always undefined + // No need to check for omitted args and template expressions, their exclusion value is always undefined if (excludeArgument[i] === false) { const arg = args[i]; const paramType = getTypeAtPosition(signature, i); @@ -9785,7 +9785,7 @@ namespace ts { case SyntaxKind.ComputedPropertyName: const nameType = checkComputedPropertyName(element.name); - if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) { + if (isTypeOfKind(nameType, TypeFlags.ESSymbol)) { return nameType; } else { @@ -9811,7 +9811,7 @@ namespace ts { */ function getEffectiveDecoratorThirdArgumentType(node: Node) { // The third argument to a decorator is either its `descriptor` for a method decorator - // or its `parameterIndex` for a paramter decorator + // or its `parameterIndex` for a parameter decorator if (node.kind === SyntaxKind.ClassDeclaration) { Debug.fail("Class decorators should not have a third synthetic argument."); return unknownType; @@ -10179,7 +10179,7 @@ namespace ts { // We exclude union types because we may have a union of function types that happen to have // no common signatures. if (isTypeAny(funcType) || (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) { - // The unknownType indicates that an error already occured (and was reported). No + // The unknownType indicates that an error already occurred (and was reported). No // need to report another error in this case. if (funcType !== unknownType && node.typeArguments) { error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); @@ -10389,10 +10389,10 @@ namespace ts { const signature = getResolvedSignature(node); if (node.expression.kind === SyntaxKind.SuperKeyword) { - const containgFunction = getContainingFunction(node.expression); + const containingFunction = getContainingFunction(node.expression); - if (containgFunction && containgFunction.kind === SyntaxKind.Constructor) { - getNodeLinks(containgFunction).flags |= NodeCheckFlags.HasSeenSuperCall; + if (containingFunction && containingFunction.kind === SyntaxKind.Constructor) { + getNodeLinks(containingFunction).flags |= NodeCheckFlags.HasSeenSuperCall; } return voidType; } @@ -10438,9 +10438,8 @@ namespace ts { const widenedType = getWidenedType(exprType); // Permit 'number[] | "foo"' to be asserted to 'string'. - const bothAreStringLike = - someConstituentTypeHasKind(targetType, TypeFlags.StringLike) && - someConstituentTypeHasKind(widenedType, TypeFlags.StringLike); + const bothAreStringLike = maybeTypeOfKind(targetType, TypeFlags.StringLike) && + maybeTypeOfKind(widenedType, TypeFlags.StringLike); if (!bothAreStringLike && !(isTypeAssignableTo(targetType, widenedType))) { checkTypeAssignableTo(exprType, targetType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other); } @@ -10695,7 +10694,7 @@ namespace ts { } // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. - if (returnType === voidType || isTypeAny(returnType) || (returnType && (returnType.flags & TypeFlags.Union) && someConstituentTypeHasKind(returnType, TypeFlags.Any | TypeFlags.Void))) { + if (returnType && maybeTypeOfKind(returnType, TypeFlags.Any | TypeFlags.Void)) { return; } @@ -10951,7 +10950,7 @@ namespace ts { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: - if (someConstituentTypeHasKind(operandType, TypeFlags.ESSymbol)) { + if (maybeTypeOfKind(operandType, TypeFlags.ESSymbol)) { error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator)); } return numberType; @@ -10983,38 +10982,47 @@ namespace ts { return numberType; } - // Just like isTypeOfKind below, except that it returns true if *any* constituent - // has this kind. - function someConstituentTypeHasKind(type: Type, kind: TypeFlags): boolean { + // Return true if type might be of the given kind. A union or intersection type might be of a given + // kind if at least one constituent type is of the given kind. + function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean { if (type.flags & kind) { return true; } if (type.flags & TypeFlags.UnionOrIntersection) { const types = (type).types; - for (const current of types) { - if (current.flags & kind) { + for (const t of types) { + if (maybeTypeOfKind(t, kind)) { return true; } } - return false; } return false; } - // Return true if type has the given flags, or is a union or intersection type composed of types that all have those flags. - function allConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean { + // Return true if type is of the given kind. A union type is of a given kind if all constituent types + // are of the given kind. An intersection type is of a given kind if at least one constituent type is + // of the given kind. + function isTypeOfKind(type: Type, kind: TypeFlags): boolean { if (type.flags & kind) { return true; } - if (type.flags & TypeFlags.UnionOrIntersection) { + if (type.flags & TypeFlags.Union) { const types = (type).types; - for (const current of types) { - if (!(current.flags & kind)) { + for (const t of types) { + if (!isTypeOfKind(t, kind)) { return false; } } return true; } + if (type.flags & TypeFlags.Intersection) { + const types = (type).types; + for (const t of types) { + if (isTypeOfKind(t, kind)) { + return true; + } + } + } return false; } @@ -11032,7 +11040,7 @@ namespace ts { // and the right operand to be of type Any or a subtype of the 'Function' interface type. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported - if (allConstituentTypesHaveKind(leftType, TypeFlags.Primitive)) { + if (isTypeOfKind(leftType, TypeFlags.Primitive)) { error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported @@ -11247,13 +11255,13 @@ namespace ts { if (rightType.flags & (TypeFlags.Undefined | TypeFlags.Null)) rightType = leftType; let resultType: Type; - if (allConstituentTypesHaveKind(leftType, TypeFlags.NumberLike) && allConstituentTypesHaveKind(rightType, TypeFlags.NumberLike)) { + if (isTypeOfKind(leftType, TypeFlags.NumberLike) && isTypeOfKind(rightType, TypeFlags.NumberLike)) { // Operands of an enum type are treated as having the primitive type Number. // If both operands are of the Number primitive type, the result is of the Number primitive type. resultType = numberType; } else { - if (allConstituentTypesHaveKind(leftType, TypeFlags.StringLike) || allConstituentTypesHaveKind(rightType, TypeFlags.StringLike)) { + if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) { // If one or both operands are of the String primitive type, the result is of the String primitive type. resultType = stringType; } @@ -11291,7 +11299,7 @@ namespace ts { case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: // Permit 'number[] | "foo"' to be asserted to 'string'. - if (someConstituentTypeHasKind(leftType, TypeFlags.StringLike) && someConstituentTypeHasKind(rightType, TypeFlags.StringLike)) { + if (maybeTypeOfKind(leftType, TypeFlags.StringLike) && maybeTypeOfKind(rightType, TypeFlags.StringLike)) { return booleanType; } if (!isTypeAssignableTo(leftType, rightType) && !isTypeAssignableTo(rightType, leftType)) { @@ -11316,8 +11324,8 @@ namespace ts { // Return true if there was no error, false if there was an error. function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean { const offendingSymbolOperand = - someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? left : - someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? right : + maybeTypeOfKind(leftType, TypeFlags.ESSymbol) ? left : + maybeTypeOfKind(rightType, TypeFlags.ESSymbol) ? right : undefined; if (offendingSymbolOperand) { error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator)); @@ -11920,7 +11928,7 @@ namespace ts { function checkConstructorDeclaration(node: ConstructorDeclaration) { // Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function. checkSignatureDeclaration(node); - // Grammar check for checking only related to constructoDeclaration + // Grammar check for checking only related to constructorDeclaration checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node); checkSourceElement(node.body); @@ -12432,7 +12440,7 @@ namespace ts { } } - // Spaces for anyting not declared a 'default export'. + // Spaces for anything not declared a 'default export'. const nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces; const commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces; @@ -12443,7 +12451,7 @@ namespace ts { for (const d of symbol.declarations) { const declarationSpaces = getDeclarationSpaces(d); - // Only error on the declarations that conributed to the intersecting spaces. + // Only error on the declarations that contributed to the intersecting spaces. if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) { error(d.name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(d.name)); } @@ -13659,7 +13667,7 @@ namespace ts { * This function does the following steps: * 1. Break up arrayOrStringType (possibly a union) into its string constituents and array constituents. * 2. Take the element types of the array constituents. - * 3. Return the union of the element types, and string if there was a string constitutent. + * 3. Return the union of the element types, and string if there was a string constituent. * * For example: * string -> string @@ -13733,7 +13741,7 @@ namespace ts { // TODO: Check that target label is valid } - function isGetAccessorWithAnnotatatedSetAccessor(node: FunctionLikeDeclaration) { + function isGetAccessorWithAnnotatedSetAccessor(node: FunctionLikeDeclaration) { return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor))); } @@ -13769,7 +13777,7 @@ namespace ts { error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } - else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func) || returnType.flags & TypeFlags.PredicateType) { + else if (func.type || isGetAccessorWithAnnotatedSetAccessor(func) || returnType.flags & TypeFlags.PredicateType) { if (isAsyncFunctionLike(func)) { const promisedType = getPromisedType(returnType); const awaitedType = checkAwaitedType(exprType, node.expression, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); @@ -13808,7 +13816,7 @@ namespace ts { let hasDuplicateDefaultClause = false; const expressionType = checkExpression(node.expression); - const expressionTypeIsStringLike = someConstituentTypeHasKind(expressionType, TypeFlags.StringLike); + const expressionTypeIsStringLike = maybeTypeOfKind(expressionType, TypeFlags.StringLike); forEach(node.caseBlock.clauses, clause => { // Grammar check for duplicate default clauses, skip if we already report duplicate default clause if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) { @@ -13832,7 +13840,7 @@ namespace ts { const expressionTypeIsAssignableToCaseType = // Permit 'number[] | "foo"' to be asserted to 'string'. - (expressionTypeIsStringLike && someConstituentTypeHasKind(caseType, TypeFlags.StringLike)) || + (expressionTypeIsStringLike && maybeTypeOfKind(caseType, TypeFlags.StringLike)) || isTypeAssignableTo(expressionType, caseType); if (!expressionTypeIsAssignableToCaseType) { @@ -14172,7 +14180,7 @@ namespace ts { Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration."); if (derived) { - // In order to resolve whether the inherited method was overriden in the base class or not, + // In order to resolve whether the inherited method was overridden in the base class or not, // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* // type declaration, derived and base resolve to the same symbol even in the case of generic classes. if (derived === base) { @@ -15074,7 +15082,7 @@ namespace ts { const kind = node.kind; if (cancellationToken) { - // Only bother checking on a few construct kinds. We don't want to be excessivly + // Only bother checking on a few construct kinds. We don't want to be excessively // hitting the cancellation token on every node we check. switch (kind) { case SyntaxKind.ModuleDeclaration: @@ -15855,13 +15863,13 @@ namespace ts { function isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean { if (symbol.flags & SymbolFlags.BlockScoped) { const links = getSymbolLinks(symbol); - if (links.isDeclaratonWithCollidingName === undefined) { + if (links.isDeclarationWithCollidingName === undefined) { const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); if (isStatementWithLocals(container)) { const nodeLinks = getNodeLinks(symbol.valueDeclaration); if (!!resolveName(container.parent, symbol.name, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) { // redeclaration - always should be renamed - links.isDeclaratonWithCollidingName = true; + links.isDeclarationWithCollidingName = true; } else if (nodeLinks.flags & NodeCheckFlags.CapturedBlockScopedBinding) { // binding is captured in the function @@ -15876,21 +15884,21 @@ namespace ts { // console.log(b()); // should print '100' // OR // - binding is declared inside loop but not in inside initializer of iteration statement or directly inside loop body - // * variables from initializer are passed to rewritted loop body as parameters so they are not captured directly + // * variables from initializer are passed to rewritten loop body as parameters so they are not captured directly // * variables that are declared immediately in loop body will become top level variable after loop is rewritten and thus // they will not collide with anything const isDeclaredInLoop = nodeLinks.flags & NodeCheckFlags.BlockScopedBindingInLoop; const inLoopInitializer = isIterationStatement(container, /*lookInLabeledStatements*/ false); const inLoopBodyBlock = container.kind === SyntaxKind.Block && isIterationStatement(container.parent, /*lookInLabeledStatements*/ false); - links.isDeclaratonWithCollidingName = !isBlockScopedContainerTopLevel(container) && (!isDeclaredInLoop || (!inLoopInitializer && !inLoopBodyBlock)); + links.isDeclarationWithCollidingName = !isBlockScopedContainerTopLevel(container) && (!isDeclaredInLoop || (!inLoopInitializer && !inLoopBodyBlock)); } else { - links.isDeclaratonWithCollidingName = false; + links.isDeclarationWithCollidingName = false; } } } - return links.isDeclaratonWithCollidingName; + return links.isDeclarationWithCollidingName; } return false; } @@ -15941,7 +15949,7 @@ namespace ts { if (target === unknownSymbol && compilerOptions.isolatedModules) { return true; } - // const enums and modules that contain only const enums are not considered values from the emit perespective + // const enums and modules that contain only const enums are not considered values from the emit perspective // unless 'preserveConstEnums' option is set to true return target !== unknownSymbol && target && @@ -16038,22 +16046,22 @@ namespace ts { else if (type.flags & TypeFlags.Any) { return TypeReferenceSerializationKind.ObjectType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.Void)) { + else if (isTypeOfKind(type, TypeFlags.Void)) { return TypeReferenceSerializationKind.VoidType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.Boolean)) { + else if (isTypeOfKind(type, TypeFlags.Boolean)) { return TypeReferenceSerializationKind.BooleanType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.NumberLike)) { + else if (isTypeOfKind(type, TypeFlags.NumberLike)) { return TypeReferenceSerializationKind.NumberLikeType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.StringLike)) { + else if (isTypeOfKind(type, TypeFlags.StringLike)) { return TypeReferenceSerializationKind.StringLikeType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.Tuple)) { + else if (isTypeOfKind(type, TypeFlags.Tuple)) { return TypeReferenceSerializationKind.ArrayLikeType; } - else if (allConstituentTypesHaveKind(type, TypeFlags.ESSymbol)) { + else if (isTypeOfKind(type, TypeFlags.ESSymbol)) { return TypeReferenceSerializationKind.ESSymbolType; } else if (isFunctionType(type)) { @@ -16801,8 +16809,8 @@ namespace ts { const seen: Map = {}; const Property = 1; const GetAccessor = 2; - const SetAccesor = 4; - const GetOrSetAccessor = GetAccessor | SetAccesor; + const SetAccessor = 4; + const GetOrSetAccessor = GetAccessor | SetAccessor; for (const prop of node.properties) { const name = prop.name; @@ -16826,7 +16834,7 @@ namespace ts { } }); - // ECMA-262 11.1.5 Object Initialiser + // ECMA-262 11.1.5 Object Initializer // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true // a.This production is contained in strict code and IsDataDescriptor(previous) is true and // IsDataDescriptor(propId.descriptor) is true. @@ -16836,7 +16844,7 @@ namespace ts { // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields let currentKind: number; if (prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment) { - // Grammar checking for computedPropertName and shorthandPropertyAssignment + // Grammar checking for computedPropertyName and shorthandPropertyAssignment checkGrammarForInvalidQuestionMark(prop, (prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); if (name.kind === SyntaxKind.NumericLiteral) { checkGrammarNumericLiteral(name); @@ -16850,7 +16858,7 @@ namespace ts { currentKind = GetAccessor; } else if (prop.kind === SyntaxKind.SetAccessor) { - currentKind = SetAccesor; + currentKind = SetAccessor; } else { Debug.fail("Unexpected syntax kind:" + prop.kind); @@ -17322,7 +17330,7 @@ namespace ts { // We are either parented by another statement, or some sort of block. // If we're in a block, we only want to really report an error once - // to prevent noisyness. So use a bit on the block to indicate if + // to prevent noisiness. So use a bit on the block to indicate if // this has already been reported, and don't report if it has. // if (node.parent.kind === SyntaxKind.Block || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 5977c84d7f4d4..d8bdd2fb52f33 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -18,7 +18,7 @@ namespace ts { referencePathsOutput: string; } - type GetSymbolAccessibilityDiagnostic = (symbolAccesibilityResult: SymbolAccessiblityResult) => SymbolAccessibilityDiagnostic; + type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic; interface EmitTextWriterWithSymbolWriter extends EmitTextWriter, SymbolWriter { getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic; @@ -51,7 +51,9 @@ namespace ts { let decreaseIndent: () => void; let writeTextOfNode: (text: string, node: Node) => void; - let writer = createAndSetNewTextWriterWithSymbolWriter(); + let writer: EmitTextWriterWithSymbolWriter; + + createAndSetNewTextWriterWithSymbolWriter(); let enclosingDeclaration: Node; let resultHasExternalModuleIndicator: boolean; @@ -174,7 +176,7 @@ namespace ts { } } - function createAndSetNewTextWriterWithSymbolWriter(): EmitTextWriterWithSymbolWriter { + function createAndSetNewTextWriterWithSymbolWriter(): void { const writer = createTextWriter(newLine); writer.trackSymbol = trackSymbol; writer.reportInaccessibleThisError = reportInaccessibleThisError; @@ -186,7 +188,6 @@ namespace ts { writer.writeParameter = writer.write; writer.writeSymbol = writer.write; setWriter(writer); - return writer; } function setWriter(newWriter: EmitTextWriterWithSymbolWriter) { @@ -252,30 +253,30 @@ namespace ts { setWriter(oldWriter); } - function handleSymbolAccessibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) { - if (symbolAccesibilityResult.accessibility === SymbolAccessibility.Accessible) { + function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { + if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) { // write the aliases - if (symbolAccesibilityResult && symbolAccesibilityResult.aliasesToMakeVisible) { - writeAsynchronousModuleElements(symbolAccesibilityResult.aliasesToMakeVisible); + if (symbolAccessibilityResult && symbolAccessibilityResult.aliasesToMakeVisible) { + writeAsynchronousModuleElements(symbolAccessibilityResult.aliasesToMakeVisible); } } else { // Report error reportedDeclarationError = true; - const errorInfo = writer.getSymbolAccessibilityDiagnostic(symbolAccesibilityResult); + const errorInfo = writer.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult); if (errorInfo) { if (errorInfo.typeName) { - emitterDiagnostics.add(createDiagnosticForNode(symbolAccesibilityResult.errorNode || errorInfo.errorNode, + emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, errorInfo.diagnosticMessage, getTextOfNodeFromSourceText(currentText, errorInfo.typeName), - symbolAccesibilityResult.errorSymbolName, - symbolAccesibilityResult.errorModuleName)); + symbolAccessibilityResult.errorSymbolName, + symbolAccessibilityResult.errorModuleName)); } else { - emitterDiagnostics.add(createDiagnosticForNode(symbolAccesibilityResult.errorNode || errorInfo.errorNode, + emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, errorInfo.diagnosticMessage, - symbolAccesibilityResult.errorSymbolName, - symbolAccesibilityResult.errorModuleName)); + symbolAccessibilityResult.errorSymbolName, + symbolAccessibilityResult.errorModuleName)); } } } @@ -548,7 +549,7 @@ namespace ts { writeAsynchronousModuleElements(nodes); } - function getDefaultExportAccessibilityDiagnostic(diagnostic: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getDefaultExportAccessibilityDiagnostic(diagnostic: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { return { diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, errorNode: node @@ -677,7 +678,7 @@ namespace ts { } writer.writeLine(); - function getImportEntityNameVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getImportEntityNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { return { diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1, errorNode: node, @@ -733,7 +734,7 @@ namespace ts { function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered - // external modules since they are indistingushable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' + // external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' // so compiler will treat them as external modules. resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; let moduleSpecifier: Node; @@ -850,7 +851,7 @@ namespace ts { writeLine(); enclosingDeclaration = prevEnclosingDeclaration; - function getTypeAliasDeclarationVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getTypeAliasDeclarationVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { return { diagnosticMessage: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1, errorNode: node.type, @@ -917,7 +918,7 @@ namespace ts { } } - function getTypeParameterConstraintVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getTypeParameterConstraintVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { // Type parameter constraints are named by user so we should always be able to name it let diagnosticMessage: DiagnosticMessage; switch (node.parent.kind) { @@ -987,7 +988,7 @@ namespace ts { write("null"); } - function getHeritageClauseVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getHeritageClauseVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { let diagnosticMessage: DiagnosticMessage; // Heritage clause is written by user so it can always be named if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { @@ -1104,10 +1105,10 @@ namespace ts { } } - function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult: SymbolAccessiblityResult) { + function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { if (node.kind === SyntaxKind.VariableDeclaration) { - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Exported_variable_0_has_or_is_using_private_name_1; @@ -1116,30 +1117,30 @@ namespace ts { else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) { // TODO(jfreeman): Deal with computed properties in error reporting. if (node.flags & NodeFlags.Static) { - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1; } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1; } else { // Interfaces cannot have types that cannot be named - return symbolAccesibilityResult.errorModuleName ? + return symbolAccessibilityResult.errorModuleName ? Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1; } } } - function getVariableDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult); + function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); return diagnosticMessage !== undefined ? { diagnosticMessage, errorNode: node, @@ -1163,8 +1164,8 @@ namespace ts { } function emitBindingElement(bindingElement: BindingElement) { - function getBindingElementTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult); + function getBindingElementTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); return diagnosticMessage !== undefined ? { diagnosticMessage, errorNode: bindingElement, @@ -1255,17 +1256,17 @@ namespace ts { } } - function getAccessorDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { let diagnosticMessage: DiagnosticMessage; if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) { // Setters have to have type named and cannot infer it so, the type should always be named if (accessorWithTypeAnnotation.parent.flags & NodeFlags.Static) { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_name_1; } else { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_private_name_1; } @@ -1278,15 +1279,15 @@ namespace ts { } else { if (accessorWithTypeAnnotation.flags & NodeFlags.Static) { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_private_name_0; } else { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_private_name_0; @@ -1385,26 +1386,26 @@ namespace ts { writeLine(); } - function getReturnTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { + function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { let diagnosticMessage: DiagnosticMessage; switch (node.kind) { case SyntaxKind.ConstructSignature: // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0; break; case SyntaxKind.CallSignature: // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0; break; case SyntaxKind.IndexSignature: // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0; break; @@ -1412,30 +1413,30 @@ namespace ts { case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: if (node.flags & NodeFlags.Static) { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0; } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0; } else { // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0; } break; case SyntaxKind.FunctionDeclaration: - diagnosticMessage = symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 : Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0; @@ -1481,8 +1482,8 @@ namespace ts { writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError); } - function getParameterDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult); + function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); return diagnosticMessage !== undefined ? { diagnosticMessage, errorNode: node, @@ -1490,53 +1491,53 @@ namespace ts { } : undefined; } - function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccesibilityResult: SymbolAccessiblityResult): DiagnosticMessage { + function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage { switch (node.parent.kind) { case SyntaxKind.Constructor: - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1; case SyntaxKind.ConstructSignature: // Interfaces cannot have parameter types that cannot be named - return symbolAccesibilityResult.errorModuleName ? + return symbolAccessibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; case SyntaxKind.CallSignature: // Interfaces cannot have parameter types that cannot be named - return symbolAccesibilityResult.errorModuleName ? + return symbolAccessibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: if (node.parent.flags & NodeFlags.Static) { - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; } else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1; } else { // Interfaces cannot have parameter types that cannot be named - return symbolAccesibilityResult.errorModuleName ? + return symbolAccessibilityResult.errorModuleName ? Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1; } case SyntaxKind.FunctionDeclaration: - return symbolAccesibilityResult.errorModuleName ? - symbolAccesibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 : Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c80883cf890a2..66d33f9d7d9e4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -577,7 +577,7 @@ namespace ts { scanner.setText(""); scanner.setOnError(undefined); - // Clear any data. We don't want to accidently hold onto it for too long. + // Clear any data. We don't want to accidentally hold onto it for too long. parseDiagnostics = undefined; sourceFile = undefined; identifiers = undefined; @@ -1442,8 +1442,8 @@ namespace ts { // We can only reuse a node if it was parsed under the same strict mode that we're // currently in. i.e. if we originally parsed a node in non-strict mode, but then // the user added 'using strict' at the top of the file, then we can't use that node - // again as the presense of strict mode may cause us to parse the tokens in the file - // differetly. + // again as the presence of strict mode may cause us to parse the tokens in the file + // differently. // // Note: we *can* reuse tokens when the strict mode changes. That's because tokens // are unaffected by strict mode. It's just the parser will decide what to do with it @@ -1456,7 +1456,7 @@ namespace ts { } // Ok, we have a node that looks like it could be reused. Now verify that it is valid - // in the currest list parsing context that we're currently at. + // in the current list parsing context that we're currently at. if (!canReuseNode(node, parsingContext)) { return undefined; } @@ -1719,7 +1719,7 @@ namespace ts { }; // Parses a comma-delimited list of elements - function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimeter?: boolean): NodeArray { + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray { const saveParsingContext = parsingContext; parsingContext |= 1 << kind; const result = >[]; @@ -1748,7 +1748,7 @@ namespace ts { // parse errors. For example, this can happen when people do things like use // a semicolon to delimit object literal members. Note: we'll have already // reported an error when we called parseExpected above. - if (considerSemicolonAsDelimeter && token === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { + if (considerSemicolonAsDelimiter && token === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { nextToken(); } continue; @@ -2221,7 +2221,7 @@ namespace ts { method.name = name; method.questionToken = questionToken; - // Method signatues don't exist in expression contexts. So they have neither + // Method signatures don't exist in expression contexts. So they have neither // [Yield] nor [Await] fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, method); parseTypeMemberSemicolon(); @@ -2719,7 +2719,7 @@ namespace ts { function isYieldExpression(): boolean { if (token === SyntaxKind.YieldKeyword) { - // If we have a 'yield' keyword, and htis is a context where yield expressions are + // If we have a 'yield' keyword, and this is a context where yield expressions are // allowed, then definitely parse out a yield expression. if (inYieldContext()) { return true; @@ -2738,7 +2738,7 @@ namespace ts { // // for now we just check if the next token is an identifier. More heuristics // can be added here later as necessary. We just need to make sure that we - // don't accidently consume something legal. + // don't accidentally consume something legal. return lookAhead(nextTokenIsIdentifierOrKeywordOrNumberOnSameLine); } @@ -2767,7 +2767,7 @@ namespace ts { } else { // if the next token is not on the same line as yield. or we don't have an '*' or - // the start of an expressin, then this is just a simple "yield" expression. + // the start of an expression, then this is just a simple "yield" expression. return finishNode(node); } } @@ -3060,7 +3060,7 @@ namespace ts { // Check the precedence to see if we should "take" this operator // - For left associative operator (all operator but **), consume the operator, // recursively call the function below, and parse binaryExpression as a rightOperand - // of the caller if the new precendence of the operator is greater then or equal to the current precendence. + // of the caller if the new precedence of the operator is greater then or equal to the current precedence. // For example: // a - b - c; // ^token; leftOperand = b. Return b to the caller as a rightOperand @@ -3069,8 +3069,8 @@ namespace ts { // a - b * c; // ^token; leftOperand = b. Return b * c to the caller as a rightOperand // - For right associative operator (**), consume the operator, recursively call the function - // and parse binaryExpression as a rightOperand of the caller if the new precendence of - // the operator is strictly grater than the current precendence + // and parse binaryExpression as a rightOperand of the caller if the new precedence of + // the operator is strictly grater than the current precedence // For example: // a ** b ** c; // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand @@ -3311,7 +3311,7 @@ namespace ts { */ function isIncrementExpression(): boolean { // This function is called inside parseUnaryExpression to decide - // whether to call parseSimpleUnaryExpression or call parseIncrmentExpression directly + // whether to call parseSimpleUnaryExpression or call parseIncrementExpression directly switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: @@ -3799,7 +3799,7 @@ namespace ts { return undefined; } - // If we have a '<', then only parse this as a arugment list if the type arguments + // If we have a '<', then only parse this as a argument list if the type arguments // are complete and we have an open paren. if we don't, rewind and return nothing. return typeArguments && canFollowTypeArgumentsInExpression() ? typeArguments @@ -3996,7 +3996,7 @@ namespace ts { node.multiLine = true; } - node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralElement, /*considerSemicolonAsDelimeter*/ true); + node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralElement, /*considerSemicolonAsDelimiter*/ true); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } @@ -4792,8 +4792,8 @@ namespace ts { // off. The grammar would look something like this: // // MemberVariableDeclaration[Yield]: - // AccessibilityModifier_opt PropertyName TypeAnnotation_opt Initialiser_opt[In]; - // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initialiser_opt[In, ?Yield]; + // AccessibilityModifier_opt PropertyName TypeAnnotation_opt Initializer_opt[In]; + // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initializer_opt[In, ?Yield]; // // The checker may still error in the static case to explicitly disallow the yield expression. property.initializer = modifiers && modifiers.flags & NodeFlags.Static @@ -5394,7 +5394,7 @@ namespace ts { // ImportSpecifier: // BindingIdentifier // IdentifierName as BindingIdentifier - // ExportSpecififer: + // ExportSpecifier: // IdentifierName // IdentifierName as IdentifierName let checkIdentifierIsKeyword = isKeyword(token) && !isIdentifier(); @@ -6238,7 +6238,7 @@ namespace ts { } // Make sure we're not trying to incrementally update a source file more than once. Once - // we do an update the original source file is considered unusbale from that point onwards. + // we do an update the original source file is considered unusable from that point onwards. // // This is because we do incremental parsing in-place. i.e. we take nodes from the old // tree and give them new positions and parents. From that point on, trusting the old @@ -6369,7 +6369,7 @@ namespace ts { // We have an element that intersects the change range in some way. It may have its // start, or its end (or both) in the changed range. We want to adjust any part // that intersects such that the final tree is in a consistent state. i.e. all - // chlidren have spans within the span of their parent, and all siblings are ordered + // children have spans within the span of their parent, and all siblings are ordered // properly. // We may need to update both the 'pos' and the 'end' of the element. @@ -6391,7 +6391,7 @@ namespace ts { // -------------------ZZZ----------------- // // In this case, any element that started in the 'X' range will keep its position. - // However any element htat started after that will have their pos adjusted to be + // However any element that started after that will have their pos adjusted to be // at the end of the new range. i.e. any node that started in the 'Y' range will // be adjusted to have their start at the end of the 'Z' range. // @@ -6416,7 +6416,7 @@ namespace ts { // -------------------ZZZ----------------- // // In this case, any element that ended in the 'X' range will keep its position. - // However any element htat ended after that will have their pos adjusted to be + // However any element that ended after that will have their pos adjusted to be // at the end of the new range. i.e. any node that ended in the 'Y' range will // be adjusted to have their end at the end of the 'Z' range. if (element.end >= changeRangeOldEnd) { @@ -6746,7 +6746,7 @@ namespace ts { // Position was within this node. Keep searching deeper to find the node. forEachChild(node, visitNode, visitArray); - // don't procede any futher in the search. + // don't proceed any further in the search. return true; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f61d983a57f58..50715d255af32 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1579,7 +1579,7 @@ namespace ts { /* @internal */ classifiableNames?: Map; // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined // It is used to resolve module names in the checker. - // Content of this fiels should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead + // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead /* @internal */ resolvedModules: Map; /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; @@ -1773,7 +1773,7 @@ namespace ts { buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void; buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; - buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void; + buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; @@ -1866,7 +1866,7 @@ namespace ts { } /* @internal */ - export interface SymbolAccessiblityResult extends SymbolVisibilityResult { + export interface SymbolAccessibilityResult extends SymbolVisibilityResult { errorModuleName?: string; // If the symbol is not visible from module, module's name } @@ -1908,7 +1908,7 @@ namespace ts { writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; - isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult; + isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult; isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; @@ -2033,7 +2033,7 @@ namespace ts { containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked - isDeclaratonWithCollidingName?: boolean; // True if symbol is block scoped redeclaration + isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration bindingElement?: BindingElement; // Binding element associated with property symbol exportsSomeValue?: boolean; // true if module exports some value (not just types) } @@ -2082,7 +2082,7 @@ namespace ts { assignmentChecks?: Map; // Cache of assignment checks hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context importOnRightSide?: Symbol; // for import declarations - import that appear on the right side - jsxFlags?: JsxFlags; // flags for knowning what kind of element/attributes we're dealing with + jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element } @@ -2187,7 +2187,7 @@ namespace ts { // Type references (TypeFlags.Reference). When a class or interface has type parameters or // a "this" type, references to the class or interface are made using type references. The - // typeArguments property specififes the types to substitute for the type parameters of the + // typeArguments property specifies the types to substitute for the type parameters of the // class or interface and optionally includes an extra element that specifies the type to // substitute for "this" in the resulting instantiation. When no extra argument is present, // the type reference itself is substituted for "this". The typeArguments property is undefined @@ -2714,7 +2714,7 @@ namespace ts { /* * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of * module name resolution) or provide implementation for methods from ModuleResolutionHost (in this case compiler - * will appply built-in module resolution logic and use members of ModuleResolutionHost to ask host specific questions). + * will apply built-in module resolution logic and use members of ModuleResolutionHost to ask host specific questions). * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just * 'throw new Error("NotImplemented")' */ @@ -2740,7 +2740,7 @@ namespace ts { getGlobalDiagnostics(): Diagnostic[]; // If fileName is provided, gets all the diagnostics associated with that file name. - // Otherwise, returns all the diagnostics (global and file associated) in this colletion. + // Otherwise, returns all the diagnostics (global and file associated) in this collection. getDiagnostics(fileName?: string): Diagnostic[]; // Gets a count of how many times this collection has been modified. This value changes diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 848b0d4e06391..ab3358e465bb0 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -746,7 +746,7 @@ namespace Harness { namespace Harness { export const libFolder = "built/local/"; - const tcServicesFileName = ts.combinePaths(libFolder, "typescriptServices.js"); + const tcServicesFileName = ts.combinePaths(libFolder, Utils.getExecutionEnvironment() === Utils.ExecutionEnvironment.Browser ? "typescriptServicesInBrowserTest.js" : "typescriptServices.js"); export const tcServicesFile = IO.readFile(tcServicesFileName); export interface SourceMapEmitterCallback { @@ -895,7 +895,8 @@ namespace Harness { { name: "includeBuiltFile", type: "string" }, { name: "fileName", type: "string" }, { name: "libFiles", type: "string" }, - { name: "noErrorTruncation", type: "boolean" } + { name: "noErrorTruncation", type: "boolean" }, + { name: "suppressOutputPathCheck", type: "boolean" } ]; let optionsIndex: ts.Map; diff --git a/src/services/services.ts b/src/services/services.ts index 6761c487c6cc8..78c0f57ccf8a9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -502,7 +502,7 @@ namespace ts { spacesToRemoveAfterAsterisk = 0; } - // Analyse text on this line + // Analyze text on this line while (pos < end && !isLineBreak(sourceFile.text.charCodeAt(pos))) { const ch = sourceFile.text.charAt(pos); if (ch === "@") { @@ -641,7 +641,7 @@ namespace ts { paramHelpStringMargin = undefined; } - // If this is the start of another tag, continue with the loop in seach of param tag with symbol name + // If this is the start of another tag, continue with the loop in search of param tag with symbol name if (sourceFile.text.charCodeAt(pos) === CharacterCodes.at) { continue; } @@ -2822,7 +2822,7 @@ namespace ts { } // Check if the language version has changed since we last created a program; if they are the same, - // it is safe to reuse the souceFiles; if not, then the shape of the AST can change, and the oldSourceFile + // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile // can not be reused. we have to dump all syntax trees and create new ones. if (!changesInCompilationSettingsAffectSyntax) { // Check if the old program had this file already @@ -2916,7 +2916,7 @@ namespace ts { } /** - * getSemanticDiagnostiscs return array of Diagnostics. If '-d' is not enabled, only report semantic errors + * getSemanticDiagnostics return array of Diagnostics. If '-d' is not enabled, only report semantic errors * If '-d' enabled, report both semantic and emitter errors */ function getSemanticDiagnostics(fileName: string): Diagnostic[] { @@ -3725,7 +3725,7 @@ namespace ts { * do not occur at the current position and have not otherwise been typed. */ function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { - const exisingImportsOrExports: Map = {}; + const existingImportsOrExports: Map = {}; for (const element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out @@ -3734,14 +3734,14 @@ namespace ts { } const name = element.propertyName || element.name; - exisingImportsOrExports[name.text] = true; + existingImportsOrExports[name.text] = true; } - if (isEmpty(exisingImportsOrExports)) { + if (isEmpty(existingImportsOrExports)) { return exportsOfModule; } - return filter(exportsOfModule, e => !lookUp(exisingImportsOrExports, e.name)); + return filter(exportsOfModule, e => !lookUp(existingImportsOrExports, e.name)); } /** @@ -4639,7 +4639,16 @@ namespace ts { // to jump to the implementation directly. if (symbol.flags & SymbolFlags.Alias) { const declaration = symbol.declarations[0]; - if (node.kind === SyntaxKind.Identifier && node.parent === declaration) { + + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from a named import. + // + if (node.kind === SyntaxKind.Identifier && + (node.parent === declaration || + (declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) { + symbol = typeChecker.getAliasedSymbol(symbol); } } @@ -5565,7 +5574,7 @@ namespace ts { } // If the symbol is an import we would like to find it if we are looking for what it imports. - // So consider it visibile outside its declaration scope. + // So consider it visible outside its declaration scope. if (symbol.flags & SymbolFlags.Alias) { return undefined; } @@ -5965,12 +5974,12 @@ namespace ts { // The search set contains at least the current symbol let result = [symbol]; - // If the symbol is an alias, add what it alaises to the list + // If the symbol is an alias, add what it aliases to the list if (isImportSpecifierSymbol(symbol)) { result.push(typeChecker.getAliasedSymbol(symbol)); } - // For export specifiers, the exported name can be refering to a local symbol, e.g.: + // For export specifiers, the exported name can be referring to a local symbol, e.g.: // import {a} from "mod"; // export {a as somethingElse} // We want the *local* declaration of 'a' as declared in the import, @@ -6006,7 +6015,7 @@ namespace ts { // If the symbol.valueDeclaration is a property parameter declaration, // we should include both parameter declaration symbol and property declaration symbol - // Parameter Declaration symbol is only visible within function scope, so the symbol is stored in contructor.locals. + // Parameter Declaration symbol is only visible within function scope, so the symbol is stored in constructor.locals. // Property Declaration symbol is a member of the class, so the symbol is stored in its class Declaration.symbol.members if (symbol.valueDeclaration && symbol.valueDeclaration.kind === SyntaxKind.Parameter && isParameterPropertyDeclaration(symbol.valueDeclaration)) { @@ -6032,9 +6041,9 @@ namespace ts { /** * Find symbol of the given property-name and add the symbol to the given result array * @param symbol a symbol to start searching for the given propertyName - * @param propertyName a name of property to serach for + * @param propertyName a name of property to search for * @param result an array of symbol of found property symbols - * @param previousIterationSymbolsCache a cache of symbol from previous iterations of calling this function to prevent infinite revisitng of the same symbol. + * @param previousIterationSymbolsCache a cache of symbol from previous iterations of calling this function to prevent infinite revisiting of the same symbol. * The value of previousIterationSymbol is undefined when the function is first called. */ function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Symbol[], @@ -7377,12 +7386,12 @@ namespace ts { // comment portion. const singleLineCommentStart = /(?:\/\/+\s*)/.source; const multiLineCommentStart = /(?:\/\*+\s*)/.source; - const anyNumberOfSpacesAndAsterixesAtStartOfLine = /(?:^(?:\s|\*)*)/.source; + const anyNumberOfSpacesAndAsterisksAtStartOfLine = /(?:^(?:\s|\*)*)/.source; // Match any of the above three TODO comment start regexps. // Note that the outermost group *is* a capture group. We want to capture the preamble // so that we can determine the starting position of the TODO comment match. - const preamble = "(" + anyNumberOfSpacesAndAsterixesAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")"; + const preamble = "(" + anyNumberOfSpacesAndAsterisksAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")"; // Takes the descriptors and forms a regexp that matches them as if they were literals. // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be: diff --git a/tests/baselines/reference/malformedTags.js b/tests/baselines/reference/malformedTags.js new file mode 100644 index 0000000000000..33b2c1e6f4179 --- /dev/null +++ b/tests/baselines/reference/malformedTags.js @@ -0,0 +1,17 @@ +//// [myFile02.js] + +/** + * Checks if `value` is classified as an `Array` object. + * + * @type Function + */ +var isArray = Array.isArray; + + +//// [myFile02.js] +/** + * Checks if `value` is classified as an `Array` object. + * + * @type Function + */ +var isArray = Array.isArray; diff --git a/tests/baselines/reference/malformedTags.symbols b/tests/baselines/reference/malformedTags.symbols new file mode 100644 index 0000000000000..df953a8249735 --- /dev/null +++ b/tests/baselines/reference/malformedTags.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/salsa/myFile02.js === + +/** + * Checks if `value` is classified as an `Array` object. + * + * @type Function + */ +var isArray = Array.isArray; +>isArray : Symbol(isArray, Decl(myFile02.js, 6, 3)) +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/malformedTags.types b/tests/baselines/reference/malformedTags.types new file mode 100644 index 0000000000000..acf442f7cba09 --- /dev/null +++ b/tests/baselines/reference/malformedTags.types @@ -0,0 +1,13 @@ +=== tests/cases/conformance/salsa/myFile02.js === + +/** + * Checks if `value` is classified as an `Array` object. + * + * @type Function + */ +var isArray = Array.isArray; +>isArray : (arg: any) => arg is any[] +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] + diff --git a/tests/baselines/reference/operatorsAndIntersectionTypes.js b/tests/baselines/reference/operatorsAndIntersectionTypes.js new file mode 100644 index 0000000000000..6f5b3d329d13c --- /dev/null +++ b/tests/baselines/reference/operatorsAndIntersectionTypes.js @@ -0,0 +1,57 @@ +//// [operatorsAndIntersectionTypes.ts] +type Guid = string & { $Guid }; // Tagged string type +type SerialNo = number & { $SerialNo }; // Tagged number type + +function createGuid() { + return "21EC2020-3AEA-4069-A2DD-08002B30309D" as Guid; +} + +function createSerialNo() { + return 12345 as SerialNo; +} + +let map1: { [x: string]: number } = {}; +let guid = createGuid(); +map1[guid] = 123; // Can with tagged string + +let map2: { [x: number]: string } = {}; +let serialNo = createSerialNo(); +map2[serialNo] = "hello"; // Can index with tagged number + +const s1 = "{" + guid + "}"; +const s2 = guid.toLowerCase(); +const s3 = guid + guid; +const s4 = guid + serialNo; +const s5 = serialNo.toPrecision(0); +const n1 = serialNo * 3; +const n2 = serialNo + serialNo; +const b1 = guid === ""; +const b2 = guid === guid; +const b3 = serialNo === 0; +const b4 = serialNo === serialNo; + + +//// [operatorsAndIntersectionTypes.js] +function createGuid() { + return "21EC2020-3AEA-4069-A2DD-08002B30309D"; +} +function createSerialNo() { + return 12345; +} +var map1 = {}; +var guid = createGuid(); +map1[guid] = 123; // Can with tagged string +var map2 = {}; +var serialNo = createSerialNo(); +map2[serialNo] = "hello"; // Can index with tagged number +var s1 = "{" + guid + "}"; +var s2 = guid.toLowerCase(); +var s3 = guid + guid; +var s4 = guid + serialNo; +var s5 = serialNo.toPrecision(0); +var n1 = serialNo * 3; +var n2 = serialNo + serialNo; +var b1 = guid === ""; +var b2 = guid === guid; +var b3 = serialNo === 0; +var b4 = serialNo === serialNo; diff --git a/tests/baselines/reference/operatorsAndIntersectionTypes.symbols b/tests/baselines/reference/operatorsAndIntersectionTypes.symbols new file mode 100644 index 0000000000000..7ffc093e4afa2 --- /dev/null +++ b/tests/baselines/reference/operatorsAndIntersectionTypes.symbols @@ -0,0 +1,100 @@ +=== tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts === +type Guid = string & { $Guid }; // Tagged string type +>Guid : Symbol(Guid, Decl(operatorsAndIntersectionTypes.ts, 0, 0)) +>$Guid : Symbol($Guid, Decl(operatorsAndIntersectionTypes.ts, 0, 22)) + +type SerialNo = number & { $SerialNo }; // Tagged number type +>SerialNo : Symbol(SerialNo, Decl(operatorsAndIntersectionTypes.ts, 0, 31)) +>$SerialNo : Symbol($SerialNo, Decl(operatorsAndIntersectionTypes.ts, 1, 26)) + +function createGuid() { +>createGuid : Symbol(createGuid, Decl(operatorsAndIntersectionTypes.ts, 1, 39)) + + return "21EC2020-3AEA-4069-A2DD-08002B30309D" as Guid; +>Guid : Symbol(Guid, Decl(operatorsAndIntersectionTypes.ts, 0, 0)) +} + +function createSerialNo() { +>createSerialNo : Symbol(createSerialNo, Decl(operatorsAndIntersectionTypes.ts, 5, 1)) + + return 12345 as SerialNo; +>SerialNo : Symbol(SerialNo, Decl(operatorsAndIntersectionTypes.ts, 0, 31)) +} + +let map1: { [x: string]: number } = {}; +>map1 : Symbol(map1, Decl(operatorsAndIntersectionTypes.ts, 11, 3)) +>x : Symbol(x, Decl(operatorsAndIntersectionTypes.ts, 11, 13)) + +let guid = createGuid(); +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) +>createGuid : Symbol(createGuid, Decl(operatorsAndIntersectionTypes.ts, 1, 39)) + +map1[guid] = 123; // Can with tagged string +>map1 : Symbol(map1, Decl(operatorsAndIntersectionTypes.ts, 11, 3)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) + +let map2: { [x: number]: string } = {}; +>map2 : Symbol(map2, Decl(operatorsAndIntersectionTypes.ts, 15, 3)) +>x : Symbol(x, Decl(operatorsAndIntersectionTypes.ts, 15, 13)) + +let serialNo = createSerialNo(); +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) +>createSerialNo : Symbol(createSerialNo, Decl(operatorsAndIntersectionTypes.ts, 5, 1)) + +map2[serialNo] = "hello"; // Can index with tagged number +>map2 : Symbol(map2, Decl(operatorsAndIntersectionTypes.ts, 15, 3)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + +const s1 = "{" + guid + "}"; +>s1 : Symbol(s1, Decl(operatorsAndIntersectionTypes.ts, 19, 5)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) + +const s2 = guid.toLowerCase(); +>s2 : Symbol(s2, Decl(operatorsAndIntersectionTypes.ts, 20, 5)) +>guid.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) + +const s3 = guid + guid; +>s3 : Symbol(s3, Decl(operatorsAndIntersectionTypes.ts, 21, 5)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) + +const s4 = guid + serialNo; +>s4 : Symbol(s4, Decl(operatorsAndIntersectionTypes.ts, 22, 5)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + +const s5 = serialNo.toPrecision(0); +>s5 : Symbol(s5, Decl(operatorsAndIntersectionTypes.ts, 23, 5)) +>serialNo.toPrecision : Symbol(Number.toPrecision, Decl(lib.d.ts, --, --)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) +>toPrecision : Symbol(Number.toPrecision, Decl(lib.d.ts, --, --)) + +const n1 = serialNo * 3; +>n1 : Symbol(n1, Decl(operatorsAndIntersectionTypes.ts, 24, 5)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + +const n2 = serialNo + serialNo; +>n2 : Symbol(n2, Decl(operatorsAndIntersectionTypes.ts, 25, 5)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + +const b1 = guid === ""; +>b1 : Symbol(b1, Decl(operatorsAndIntersectionTypes.ts, 26, 5)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) + +const b2 = guid === guid; +>b2 : Symbol(b2, Decl(operatorsAndIntersectionTypes.ts, 27, 5)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) +>guid : Symbol(guid, Decl(operatorsAndIntersectionTypes.ts, 12, 3)) + +const b3 = serialNo === 0; +>b3 : Symbol(b3, Decl(operatorsAndIntersectionTypes.ts, 28, 5)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + +const b4 = serialNo === serialNo; +>b4 : Symbol(b4, Decl(operatorsAndIntersectionTypes.ts, 29, 5)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) +>serialNo : Symbol(serialNo, Decl(operatorsAndIntersectionTypes.ts, 16, 3)) + diff --git a/tests/baselines/reference/operatorsAndIntersectionTypes.types b/tests/baselines/reference/operatorsAndIntersectionTypes.types new file mode 100644 index 0000000000000..70611776fc1cd --- /dev/null +++ b/tests/baselines/reference/operatorsAndIntersectionTypes.types @@ -0,0 +1,132 @@ +=== tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts === +type Guid = string & { $Guid }; // Tagged string type +>Guid : string & { $Guid: any; } +>$Guid : any + +type SerialNo = number & { $SerialNo }; // Tagged number type +>SerialNo : number & { $SerialNo: any; } +>$SerialNo : any + +function createGuid() { +>createGuid : () => string & { $Guid: any; } + + return "21EC2020-3AEA-4069-A2DD-08002B30309D" as Guid; +>"21EC2020-3AEA-4069-A2DD-08002B30309D" as Guid : string & { $Guid: any; } +>"21EC2020-3AEA-4069-A2DD-08002B30309D" : string +>Guid : string & { $Guid: any; } +} + +function createSerialNo() { +>createSerialNo : () => number & { $SerialNo: any; } + + return 12345 as SerialNo; +>12345 as SerialNo : number & { $SerialNo: any; } +>12345 : number +>SerialNo : number & { $SerialNo: any; } +} + +let map1: { [x: string]: number } = {}; +>map1 : { [x: string]: number; } +>x : string +>{} : { [x: string]: undefined; } + +let guid = createGuid(); +>guid : string & { $Guid: any; } +>createGuid() : string & { $Guid: any; } +>createGuid : () => string & { $Guid: any; } + +map1[guid] = 123; // Can with tagged string +>map1[guid] = 123 : number +>map1[guid] : number +>map1 : { [x: string]: number; } +>guid : string & { $Guid: any; } +>123 : number + +let map2: { [x: number]: string } = {}; +>map2 : { [x: number]: string; } +>x : number +>{} : { [x: number]: undefined; } + +let serialNo = createSerialNo(); +>serialNo : number & { $SerialNo: any; } +>createSerialNo() : number & { $SerialNo: any; } +>createSerialNo : () => number & { $SerialNo: any; } + +map2[serialNo] = "hello"; // Can index with tagged number +>map2[serialNo] = "hello" : string +>map2[serialNo] : string +>map2 : { [x: number]: string; } +>serialNo : number & { $SerialNo: any; } +>"hello" : string + +const s1 = "{" + guid + "}"; +>s1 : string +>"{" + guid + "}" : string +>"{" + guid : string +>"{" : string +>guid : string & { $Guid: any; } +>"}" : string + +const s2 = guid.toLowerCase(); +>s2 : string +>guid.toLowerCase() : string +>guid.toLowerCase : () => string +>guid : string & { $Guid: any; } +>toLowerCase : () => string + +const s3 = guid + guid; +>s3 : string +>guid + guid : string +>guid : string & { $Guid: any; } +>guid : string & { $Guid: any; } + +const s4 = guid + serialNo; +>s4 : string +>guid + serialNo : string +>guid : string & { $Guid: any; } +>serialNo : number & { $SerialNo: any; } + +const s5 = serialNo.toPrecision(0); +>s5 : string +>serialNo.toPrecision(0) : string +>serialNo.toPrecision : (precision?: number) => string +>serialNo : number & { $SerialNo: any; } +>toPrecision : (precision?: number) => string +>0 : number + +const n1 = serialNo * 3; +>n1 : number +>serialNo * 3 : number +>serialNo : number & { $SerialNo: any; } +>3 : number + +const n2 = serialNo + serialNo; +>n2 : number +>serialNo + serialNo : number +>serialNo : number & { $SerialNo: any; } +>serialNo : number & { $SerialNo: any; } + +const b1 = guid === ""; +>b1 : boolean +>guid === "" : boolean +>guid : string & { $Guid: any; } +>"" : string + +const b2 = guid === guid; +>b2 : boolean +>guid === guid : boolean +>guid : string & { $Guid: any; } +>guid : string & { $Guid: any; } + +const b3 = serialNo === 0; +>b3 : boolean +>serialNo === 0 : boolean +>serialNo : number & { $SerialNo: any; } +>0 : number + +const b4 = serialNo === serialNo; +>b4 : boolean +>serialNo === serialNo : boolean +>serialNo : number & { $SerialNo: any; } +>serialNo : number & { $SerialNo: any; } + diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts b/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts new file mode 100644 index 0000000000000..5172ccfe21fd7 --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts @@ -0,0 +1,12 @@ +class A { + blub = 6; +} + + +class B extends A { + constructor(public x: number) { + "use strict"; + 'someStringForEgngInject'; + super() + } +} diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts b/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts new file mode 100644 index 0000000000000..e62f753d725f9 --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts @@ -0,0 +1,13 @@ +// @target: ES6 +class A { + blub = 6; +} + + +class B extends A { + constructor(public x: number) { + "use strict"; + 'someStringForEgngInject'; + super() + } +} diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1.ts b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1.ts new file mode 100644 index 0000000000000..52d41c3731a74 --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1.ts @@ -0,0 +1,15 @@ +class A { + blub = 6; +} + + +class B extends A { + + blub = 12; + + constructor() { + "use strict"; + 'someStringForEgngInject'; + super() + } +} \ No newline at end of file diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1ES6.ts b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1ES6.ts new file mode 100644 index 0000000000000..f6c7164ae7599 --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclaration1ES6.ts @@ -0,0 +1,15 @@ +// @target: ES6 +class A { + blub = 6; +} + + +class B extends A { + + blub = 12; + + constructor() { + 'someStringForEgngInject'; + super() + } +} \ No newline at end of file diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts new file mode 100644 index 0000000000000..a2d8bf8473f28 --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts @@ -0,0 +1,13 @@ +class A { + blub = 6; +} + + +class B extends A { + blah = 2; + constructor(public x: number) { + "use strict"; + 'someStringForEgngInject'; + super() + } +} \ No newline at end of file diff --git a/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1ES6.ts b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1ES6.ts new file mode 100644 index 0000000000000..8bd8420f8f74d --- /dev/null +++ b/tests/cases/compiler/emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1ES6.ts @@ -0,0 +1,14 @@ +// @target: ES6 +class A { + blub = 6; +} + + +class B extends A { + blah = 2; + constructor(public x: number) { + "use strict"; + 'someStringForEgngInject'; + super() + } +} \ No newline at end of file diff --git a/tests/cases/conformance/salsa/malformedTags.ts b/tests/cases/conformance/salsa/malformedTags.ts new file mode 100644 index 0000000000000..1128ce73f1547 --- /dev/null +++ b/tests/cases/conformance/salsa/malformedTags.ts @@ -0,0 +1,10 @@ +// @allowJS: true +// @suppressOutputPathCheck: true + +// @filename: myFile02.js +/** + * Checks if `value` is classified as an `Array` object. + * + * @type Function + */ +var isArray = Array.isArray; diff --git a/tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts b/tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts new file mode 100644 index 0000000000000..5689d2218401a --- /dev/null +++ b/tests/cases/conformance/types/intersection/operatorsAndIntersectionTypes.ts @@ -0,0 +1,30 @@ +type Guid = string & { $Guid }; // Tagged string type +type SerialNo = number & { $SerialNo }; // Tagged number type + +function createGuid() { + return "21EC2020-3AEA-4069-A2DD-08002B30309D" as Guid; +} + +function createSerialNo() { + return 12345 as SerialNo; +} + +let map1: { [x: string]: number } = {}; +let guid = createGuid(); +map1[guid] = 123; // Can with tagged string + +let map2: { [x: number]: string } = {}; +let serialNo = createSerialNo(); +map2[serialNo] = "hello"; // Can index with tagged number + +const s1 = "{" + guid + "}"; +const s2 = guid.toLowerCase(); +const s3 = guid + guid; +const s4 = guid + serialNo; +const s5 = serialNo.toPrecision(0); +const n1 = serialNo * 3; +const n2 = serialNo + serialNo; +const b1 = guid === ""; +const b2 = guid === guid; +const b3 = serialNo === 0; +const b4 = serialNo === serialNo; diff --git a/tests/cases/fourslash/goToDefinitionImportedNames3.ts b/tests/cases/fourslash/goToDefinitionImportedNames3.ts index d55137575efa6..70fce1749e4e2 100644 --- a/tests/cases/fourslash/goToDefinitionImportedNames3.ts +++ b/tests/cases/fourslash/goToDefinitionImportedNames3.ts @@ -31,7 +31,7 @@ goTo.file("e.ts"); goTo.marker('classReference'); goTo.definition(); -verify.caretAtMarker('classAliasDefinition'); +verify.caretAtMarker('classDefinition'); goTo.marker('classAliasDefinition'); goTo.definition(); From 3f0c283004d3757d1a4c81f0c209d6c87dc80410 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 11 Feb 2016 16:33:07 +0100 Subject: [PATCH 21/49] Fix typos after merge --- src/compiler/binder.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5e893f4489b56..5528df97c51e8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -65,8 +65,8 @@ namespace ts { reachability: Reachability.Reachable, previous: undefined }; - const defaultUnintialized: Flow = { - reachability: Reachability.Unintialized, + const defaultUninitialized: Flow = { + reachability: Reachability.Uninitialized, previous: undefined }; @@ -1793,7 +1793,7 @@ namespace ts { // reachability checks function pushLabel(node: Node) { - return labelStack.push({ continueFlow: defaultUnintialized, breakFlow: defaultUnintialized, node }) - 1; + return labelStack.push({ continueFlow: defaultUninitialized, breakFlow: defaultUninitialized, node }) - 1; } function pushNamedLabel(name: Identifier, node: LabeledStatement): boolean { initializeReachabilityStateIfNecessary(); @@ -1822,8 +1822,8 @@ namespace ts { const labelState = labelStack.pop(); if (!options.allowUnusedLabels - && labelState.breakFlow.reachability === Reachability.Unintialized - && labelState.continueFlow.reachability === Reachability.Unintialized) { + && labelState.breakFlow.reachability === Reachability.Uninitialized + && labelState.continueFlow.reachability === Reachability.Uninitialized) { file.bindDiagnostics.push(createDiagnosticForNode(label, Diagnostics.Unused_label)); } From ac2f547c2f168817b585c139d74176a2d93e68ec Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 12 Feb 2016 11:27:15 +0100 Subject: [PATCH 22/49] Remove old assertion --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 37f8addc90fcd..a836d602aa6d4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6903,7 +6903,6 @@ namespace ts { function getNarrowedTypeOfSymbol(symbol: Symbol, location: Node) { const initialType = getTypeOfSymbol(symbol); const isUnion = (initialType.flags & TypeFlags.Union) !== 0; - Debug.assert(location.kind === SyntaxKind.Identifier, "node in getNarrowedTypeOfSymbol should be an identifier"); // Only narrow when symbol is variable of type any or an object, union, or type parameter type if (!location || !(symbol.flags & SymbolFlags.Variable)) return initialType; From 0a91991915beb219de960ed94c35373ee46f39d2 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 12 Feb 2016 22:29:44 +0100 Subject: [PATCH 23/49] Keep narrowed types in a loop Previously, narrowing would be removed in the following loop: ``` let x: string | number; x = 42; for (;x;) { x; } ``` With this change, narrowing is preserved --- src/compiler/checker.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a836d602aa6d4..6cbf47458c01e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6957,7 +6957,7 @@ namespace ts { if (fallback) { return initialType; } - return types.length === 0 ? initialType : getUnionType(types); + return getUnionType(types); function handleGuards(node: Identifier, guards: BranchFlow[]) { if (!node && guards.length === 0) { @@ -6965,7 +6965,7 @@ namespace ts { } let type = getType(node, true); if (type === undefined) { - type = initialType; + return false; } for (let i = guards.length - 1; i >= 0; i--) { const { expression, trueBranch } = guards[i]; @@ -6974,7 +6974,9 @@ namespace ts { if (type === initialType) { return true; } - types.push(type); + if (type !== emptyUnionType) { + types.push(type); + } return false; } } From 67913d8dea3917bae1cf8cec399ceb26c6847d07 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 13 Feb 2016 11:38:18 +0100 Subject: [PATCH 24/49] Fix narrowing of union type assigned to a union type ``` let x: string | number | boolean; x = 42 || true; ``` Narrowing would fail here, resulting in the initial type. With this change, x will be narrowed to number | boolean. --- src/compiler/checker.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7435c485b0bf9..b127aa544fa00 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7246,14 +7246,19 @@ namespace ts { return checkRightHandSideOfForOf((parent).expression, false); } } - function narrowTypeByAssignment(type: Type) { + function narrowTypeByAssignment(assignedType: Type) { // Narrow union types only if (!isUnion) return initialType; + + const assignedTypes = (assignedType.flags & TypeFlags.Union) ? ( assignedType).types : [assignedType]; + const constituentTypes = ( initialType).types; const assignableTypes: Type[] = []; for (const constituentType of constituentTypes) { - if (isTypeAssignableTo(type, constituentType)) { - assignableTypes.push(constituentType); + for (const type of assignedTypes) { + if (isTypeAssignableTo(type, constituentType)) { + assignableTypes.push(constituentType); + } } } if (assignableTypes.length === 0) { From 074a05d5d647de9e62fa1b596332ba4e224ddd82 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 26 Feb 2016 11:48:01 +0100 Subject: [PATCH 25/49] Add more caching --- src/compiler/checker.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dfa29c8bc2cd1..a1ec9be31b53e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7015,6 +7015,7 @@ namespace ts { } const visited: FlowMarkerTarget[] = []; + let loop = false; const narrowedType = getType(location, false); if (saveLocalType) { if ((location).narrowingState === NarrowingState.Failed) { @@ -7028,22 +7029,31 @@ namespace ts { function getType(where: Node, after: boolean) { if (where === undefined) return initialType; - if (visited.indexOf(where) !== -1) return undefined; + if (visited.indexOf(where) !== -1) { + loop = true; + return undefined; + } const isIdentifier = where.kind === SyntaxKind.Identifier; - if (isIdentifier && after) { - const assignment = getAssignedTypeAtLocation(where); - if (assignment) { - return narrowTypeByAssignment(assignment); + if (isIdentifier) { + if (after) { + const assignment = getAssignedTypeAtLocation(where); + if (assignment) { + return narrowTypeByAssignment(assignment); + } + } + if ((where).narrowingState === NarrowingState.Done) { + return (where).localType; } } let types: Type[] = []; visited.push(where); const fallback = getPreviousOccurences(symbol, where, handleGuards); visited.pop(); - if (fallback) { - return initialType; + const type = fallback ? initialType : getUnionType(types); + if (!loop && (where).narrowingState !== NarrowingState.Failed) { + (where).localType = type; } - return getUnionType(types); + return type; function handleGuards(node: Identifier, guards: BranchFlow[]) { if (!node && guards.length === 0) { @@ -7289,7 +7299,7 @@ namespace ts { const { parent } = node; if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { if ((parent).initializer && !isUnion) { - return getTypeOfExpression((parent).initializer); + return checkExpressionCached((parent).initializer); } else { // This catches these constructs: @@ -7302,7 +7312,7 @@ namespace ts { } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { if (!isUnion) return initialType; - return getTypeOfExpression((parent).right); + return checkExpressionCached((parent).right); } if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { // for (x in z) {} From 2c57ee5290cfa5cc298f22a1bae2c4d8c000c430 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 26 Feb 2016 17:31:13 +0100 Subject: [PATCH 26/49] Use cache to speed up multiple paths to same node in flow graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Speeds up compilation of this code: ``` x = 42; if (…) {} if (…) {} if (…) {} … if (…) {} x ``` Without this change, there would be `2^n` paths from `x` on the last line to `x` on the first line. Now the type of `x` before the if statement is cached, so less work is done (`O(2n)` instead of `O(2^n)`) --- src/compiler/binder.ts | 2 ++ src/compiler/checker.ts | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6d603b604f37f..bbc639ec701da 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -821,6 +821,7 @@ namespace ts { const isOr = node.operatorToken.kind === SyntaxKind.BarBarToken; if (isAnd || isOr) { + bindFlowMarker(node); bind(node.left); const preRightState = currentReachabilityState; bindBranchFlow(node, node.left, isAnd); @@ -837,6 +838,7 @@ namespace ts { } function bindConditionalExpression(node: ConditionalExpression) { + bindFlowMarker(node); bind(node.condition); const postConditionState = currentReachabilityState; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a1ec9be31b53e..85c3dfea4a940 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6938,7 +6938,7 @@ namespace ts { } } - function getPreviousOccurences(symbol: Symbol, where: Node, callback: (node: Identifier, guards: BranchFlow[]) => boolean) { + function getPreviousOccurences(symbol: Symbol, where: Node, callback: (node: Node, guards: BranchFlow[]) => boolean) { let stop = false; const visited: { [id: number]: boolean } = {}; const guards: BranchFlow[] = []; @@ -6950,21 +6950,22 @@ namespace ts { if (stop) return; let isGuard = false; let nodeId: number; - if (( location).kind !== undefined) { + if ((location).kind !== undefined) { nodeId = getNodeId(location); if (visited[nodeId]) return; - const otherSymbol = getSymbolAtLocation( location); - if (location !== where && ( location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id) { + const otherSymbol = getSymbolAtLocation(location); + const isSameIdentifier = location !== where && (location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; + if (isSameIdentifier || isBranchStart(location)) { stop = callback( location, guards); return; } visited[nodeId] = true; } else { - if (guards.indexOf( location) !== -1) return; + if (guards.indexOf(location) !== -1) return; isGuard = true; - guards.push( location); + guards.push(location); } if (location.previous === undefined) { // We cannot do analysis in a catch or finally block @@ -6983,6 +6984,11 @@ namespace ts { } return; } + 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 @@ -7014,6 +7020,7 @@ namespace ts { if (getSourceFileOfNode(declaration) !== getSourceFileOfNode(location)) return initialType; } const visited: FlowMarkerTarget[] = []; + const cache: { [nodeId: number]: Type } = {}; let loop = false; const narrowedType = getType(location, false); @@ -7029,6 +7036,10 @@ namespace ts { function getType(where: Node, after: 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; @@ -7038,7 +7049,7 @@ namespace ts { if (after) { const assignment = getAssignedTypeAtLocation(where); if (assignment) { - return narrowTypeByAssignment(assignment); + return cache[whereId] = narrowTypeByAssignment(assignment); } } if ((where).narrowingState === NarrowingState.Done) { @@ -7050,9 +7061,12 @@ namespace ts { const fallback = getPreviousOccurences(symbol, where, handleGuards); visited.pop(); const type = fallback ? initialType : getUnionType(types); - if (!loop && (where).narrowingState !== NarrowingState.Failed) { + if (!loop && isIdentifier && (where).narrowingState !== NarrowingState.Failed) { (where).localType = type; } + if (after) { + cache[whereId] = type; + } return type; function handleGuards(node: Identifier, guards: BranchFlow[]) { From 927c56266af3d34fc72b85a93827a58ebb757e27 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 29 Feb 2016 11:54:50 +0100 Subject: [PATCH 27/49] Don't error when assigning to narrowed variable using destructuring --- src/compiler/checker.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 85c3dfea4a940..be7266a01ff06 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7000,7 +7000,7 @@ namespace ts { if (!location || !(symbol.flags & SymbolFlags.Variable)) return initialType; if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; - if (location.parent.kind === SyntaxKind.VariableDeclaration && ( location.parent).name === location) return initialType; + if (isLeftHandSideOfAssignment(location)) return initialType; let saveLocalType = false; if (location.kind === SyntaxKind.Identifier) { @@ -7090,6 +7090,22 @@ namespace ts { return false; } } + + 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.PropertyAssignment || parent.kind === SyntaxKind.ShorthandPropertyAssignment) && (parent).name === node) { + return isLeftHandSideOfAssignment(parent.parent); + } + else if (parent.kind === SyntaxKind.ArrayLiteralExpression) { + return isLeftHandSideOfAssignment(parent); + } + } function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // Check that we have 'typeof ' on the left and string literal on the right From 8673a46ee50bf2afde13cbc12b4edd19252f4754 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 7 Mar 2016 07:53:50 +0100 Subject: [PATCH 28/49] Narrow after destructuring assignment --- src/compiler/binder.ts | 7 +++- src/compiler/checker.ts | 85 +++++++++++++++++++++++++++++++++++------ src/compiler/parser.ts | 4 +- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index bbc639ec701da..4b46c97766a38 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -828,7 +828,7 @@ namespace ts { bind(node.right); currentReachabilityState = or(currentReachabilityState, preRightState); } - else if (isAssignmentOperator(node.operatorToken.kind) && node.left.kind === SyntaxKind.Identifier) { + 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); } @@ -1752,7 +1752,10 @@ namespace ts { function bindIdentifier(node: Identifier) { checkStrictModeIdentifier(node); - if (isExpression(node) || (node.parent.kind === SyntaxKind.VariableDeclaration && (node.parent).name === 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; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 079ad59e3a551..2b501aaa5cb40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7002,8 +7002,12 @@ namespace ts { nodeId = getNodeId(location); if (visited[nodeId]) return; - const otherSymbol = getSymbolAtLocation(location); - const isSameIdentifier = location !== where && (location).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; + let isSameIdentifier = false; + if ((location).kind === SyntaxKind.Identifier) { + const identifier = location; + const otherSymbol = resolveName(identifier, identifier.text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined) + isSameIdentifier = identifier !== where && (identifier).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; + } if (isSameIdentifier || isBranchStart(location)) { stop = callback( location, guards); return; @@ -7147,12 +7151,18 @@ namespace ts { else if (parent.kind === SyntaxKind.VariableDeclaration && (parent).name === node) { return hasInitializer(parent); } - else if ((parent.kind === SyntaxKind.PropertyAssignment || parent.kind === SyntaxKind.ShorthandPropertyAssignment) && (parent).name === node) { + 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); + } } function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -7370,14 +7380,20 @@ namespace ts { } return type; } - function getAssignedTypeAtLocation(node: Identifier) { + function getAssignedTypeAtLocation(node: Node): Type { // 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) { - return checkExpressionCached((parent).initializer); + 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: @@ -7385,27 +7401,74 @@ namespace ts { // - for (let y in z) {} // - for (let y of z) {} // - Other cases where `initialType` is not a union type - return initialType; + return unknownType; } } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { - if (!isUnion) return initialType; + if (!isUnion) return unknownType; return checkExpressionCached((parent).right); } if (parent.kind === SyntaxKind.ForInStatement && (parent).initializer === node) { // for (x in z) {} - if (!isUnion) return initialType; + if (!isUnion) return unknownType; return globalStringType; } if (parent.kind === SyntaxKind.ForOfStatement && (parent).initializer === node) { // for (x of z) {} - if (!isUnion) return initialType; + 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; + return getPropertyAssignmentType(type, parent) || unknownType; + } + + 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, property: PropertyAssignment | ShorthandPropertyAssignment) { + const name = property.name; + if (name.kind === SyntaxKind.Identifier) { + const property = getPropertyOfType(parentType, (name).text); + if (property) return getTypeOfSymbol(property); + } + return undefined; } function narrowTypeByAssignment(assignedType: Type) { // Narrow union types only - if (!isUnion) return initialType; + if (!isUnion || assignedType === anyType || assignedType === unknownType) return initialType; const assignedTypes = (assignedType.flags & TypeFlags.Union) ? ( assignedType).types : [assignedType]; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db9639156a58e..3432ff7512a06 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1824,7 +1824,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); @@ -3648,7 +3648,7 @@ namespace ts { let elementName: EntityName = parseIdentifierName(); while (parseOptional(SyntaxKind.DotToken)) { scanJsxIdentifier(); - const node = createNode(SyntaxKind.QualifiedName, elementName.pos); + const node: QualifiedName = createNode(SyntaxKind.QualifiedName, elementName.pos); node.left = elementName; node.right = parseIdentifierName(); elementName = finishNode(node); From 09e4302bbcd4902b1e238859a343703a0a6cd4b7 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 9 Mar 2016 17:40:20 +0100 Subject: [PATCH 29/49] Rewrite recursion to loop, reduces check time --- src/compiler/checker.ts | 60 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b501aaa5cb40..88f44896b7e5b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6990,17 +6990,25 @@ namespace ts { let stop = false; const visited: { [id: number]: boolean } = {}; const guards: BranchFlow[] = []; - - worker(where); - return stop; - - function worker(location: FlowMarkerTarget) { - if (stop) return; + + 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]) return; + if (visited[nodeId]) continue; let isSameIdentifier = false; if ((location).kind === SyntaxKind.Identifier) { @@ -7009,33 +7017,41 @@ namespace ts { isSameIdentifier = identifier !== where && (identifier).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; } if (isSameIdentifier || isBranchStart(location)) { - stop = callback( location, guards); - return; + if (callback( location, guards)) { + stop = true; + break; + } + else { + continue; + } } visited[nodeId] = true; } else { - if (guards.indexOf(location) !== -1) return; + 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 - stop = callback(undefined, guards); - if (!isGuard) visited[nodeId] = false; - return; - } - for (const item of location.previous) { - worker(item); - } - if (isGuard) { - guards.pop(); + if (callback(undefined, guards)) { + stop = true; + break; + } + else { + if (!isGuard) visited[nodeId] = false; + continue; + } } - else { - visited[nodeId] = false; + stack.push([location, false]); + for (let i = location.previous.length - 1; i >= 0; i--) { + const item = location.previous[i]; + stack.push([item, true]); } - return; } + + return stop; + function isBranchStart(node: Node) { const guard = guards[guards.length - 1]; if (!guard) return false; From 8331915ce9773b17fcfe2b29f2dceabbd7a282ad Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sat, 12 Mar 2016 10:30:47 +0100 Subject: [PATCH 30/49] Fix issues with destructuring assignments --- src/compiler/checker.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fe74b31e91630..e1636b73fb553 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7262,6 +7262,10 @@ namespace ts { 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 narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { @@ -11751,14 +11755,6 @@ namespace ts { // Use default messages if (ok) { let leftOriginalType = leftType; - if (left.kind === SyntaxKind.Identifier) { - const symbol = getSymbolAtLocation(left); - if (!symbol) { - leftOriginalType = anyType; - } else { - leftOriginalType = getTypeOfSymbol(symbol); - } - } // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported checkTypeAssignableTo(valueType, leftOriginalType, left, /*headMessage*/ undefined); } From c1285824e962291b45efb542cf94b7e252352976 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Sun, 13 Mar 2016 22:01:56 +0100 Subject: [PATCH 31/49] Fix narrowing of JSX tags They caused infinite recursion without this change --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e1636b73fb553..72b1b686a1432 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7096,7 +7096,7 @@ namespace ts { let isSameIdentifier = false; if ((location).kind === SyntaxKind.Identifier) { const identifier = location; - const otherSymbol = resolveName(identifier, identifier.text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined) + const otherSymbol = resolveName(identifier, identifier.text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); isSameIdentifier = identifier !== where && (identifier).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; } if (isSameIdentifier || isBranchStart(location)) { @@ -7155,7 +7155,7 @@ namespace ts { let saveLocalType = false; if (location.kind === SyntaxKind.Identifier) { - const locationSymbol = getSymbolAtLocation(location); + const locationSymbol = resolveName(location, (location).text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); if (locationSymbol && locationSymbol.id === symbol.id) { if ((location).narrowingState === NarrowingState.Done) return (location).localType; if ((location).narrowingState === NarrowingState.Narrowing) { From 19b3f90aae39a450c3e9f505d9c805edc6f40022 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 14 Mar 2016 11:31:11 +0100 Subject: [PATCH 32/49] Use initial type when narrowing resulted in empty type --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 72b1b686a1432..7b34d92304028 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7211,7 +7211,7 @@ namespace ts { visited.push(where); const fallback = getPreviousOccurences(symbol, where, handleGuards); visited.pop(); - const type = fallback ? initialType : getUnionType(types); + const type = fallback || types.length === 0 ? initialType : getUnionType(types); if (!loop && isIdentifier && (where).narrowingState !== NarrowingState.Failed) { (where).localType = type; } From 59b543f25424cc3327d95019adb1aba1940b15a9 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 14 Mar 2016 11:49:47 +0100 Subject: [PATCH 33/49] Move narrowing info from node to NodeLinks --- src/compiler/binder.ts | 2 -- src/compiler/checker.ts | 28 ++++++++++++++++------------ src/compiler/types.ts | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 66350335ab5a3..ce803ad4ce485 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1799,8 +1799,6 @@ namespace ts { if (node.parent.kind === SyntaxKind.PropertyAccessExpression && ( node.parent).name === node) { return; } - node.narrowingState = NarrowingState.Uninitialized; - node.localType = undefined; bindFlowMarker(node); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7b34d92304028..c84ae47b9e033 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7154,16 +7154,18 @@ namespace ts { if (isLeftHandSideOfAssignment(location)) return initialType; let saveLocalType = false; + let nodeLinks: NodeLinks; if (location.kind === SyntaxKind.Identifier) { const locationSymbol = resolveName(location, (location).text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); if (locationSymbol && locationSymbol.id === symbol.id) { - if ((location).narrowingState === NarrowingState.Done) return (location).localType; - if ((location).narrowingState === NarrowingState.Narrowing) { - (location).narrowingState = NarrowingState.Failed; - return (location).localType = initialType; + nodeLinks = getNodeLinks(location); + if (nodeLinks.identifierNarrowingState === NarrowingState.Done) return nodeLinks.identifierLocalType; + if (nodeLinks.identifierNarrowingState === NarrowingState.Narrowing) { + nodeLinks.identifierNarrowingState = NarrowingState.Failed; + return nodeLinks.identifierLocalType = initialType; } saveLocalType = true; - (location).narrowingState = NarrowingState.Narrowing; + nodeLinks.identifierNarrowingState = NarrowingState.Narrowing; } } if (!symbol.declarations) return initialType; @@ -7176,12 +7178,12 @@ namespace ts { let loop = false; const narrowedType = getType(location, false); if (saveLocalType) { - if ((location).narrowingState === NarrowingState.Failed) { + if (nodeLinks.identifierNarrowingState === NarrowingState.Failed) { // During recursion, the narrowing of this node failed. return initialType; } - (location).narrowingState = NarrowingState.Done; - (location).localType = narrowedType; + nodeLinks.identifierNarrowingState = NarrowingState.Done; + nodeLinks.identifierLocalType = narrowedType; } return narrowedType; @@ -7196,6 +7198,7 @@ namespace ts { return undefined; } const isIdentifier = where.kind === SyntaxKind.Identifier; + let identifierNodeLinks: NodeLinks; if (isIdentifier) { if (after) { const assignment = getAssignedTypeAtLocation(where); @@ -7203,8 +7206,9 @@ namespace ts { return cache[whereId] = narrowTypeByAssignment(assignment); } } - if ((where).narrowingState === NarrowingState.Done) { - return (where).localType; + identifierNodeLinks = getNodeLinks(where); + if (identifierNodeLinks.identifierNarrowingState === NarrowingState.Done) { + return identifierNodeLinks.identifierLocalType; } } let types: Type[] = []; @@ -7212,8 +7216,8 @@ namespace ts { const fallback = getPreviousOccurences(symbol, where, handleGuards); visited.pop(); const type = fallback || types.length === 0 ? initialType : getUnionType(types); - if (!loop && isIdentifier && (where).narrowingState !== NarrowingState.Failed) { - (where).localType = type; + if (!loop && isIdentifier && identifierNodeLinks.identifierNarrowingState !== NarrowingState.Failed) { + identifierNodeLinks.identifierLocalType = type; } if (after) { cache[whereId] = type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d6e56bef60fe7..ffbde404497fd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -491,8 +491,6 @@ namespace ts { export interface Identifier extends PrimaryExpression { text: string; // Text of identifier (with escapes converted to characters) originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later - /* @internal */ narrowingState?: NarrowingState; - /* @internal */ localType?: Type; } // @kind(SyntaxKind.QualifiedName) @@ -2099,6 +2097,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 + identifierNarrowingState?: NarrowingState; + identifierLocalType?: Type; } export const enum TypeFlags { From c0061c065d3f1edf1d75a458c929bdccbfe2deaf Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Mon, 14 Mar 2016 12:29:31 +0100 Subject: [PATCH 34/49] Allow narrowing in nested functions --- src/compiler/binder.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ce803ad4ce485..494209439859a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -509,7 +509,19 @@ namespace ts { savedImplicitLabels = implicitLabels; savedHasExplicitReturn = hasExplicitReturn; - currentReachabilityState = defaultReachable; + /* + * 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; } From 096c70a657656e12561e21c4ca0ef7fc8576b008 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 09:08:52 +0100 Subject: [PATCH 35/49] Narrow after destructuring assignments with initializers --- src/compiler/checker.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c84ae47b9e033..e79c740b610f5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7512,8 +7512,15 @@ namespace ts { } } if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { + // x = y if (!isUnion) return unknownType; - return checkExpressionCached((parent).right); + 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) {} @@ -7536,7 +7543,13 @@ namespace ts { // {x} = y; const type = getAssignedTypeAtLocation(parent.parent); if (!type) return undefined; - return getPropertyAssignmentType(type, parent) || unknownType; + 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) { @@ -7565,13 +7578,13 @@ namespace ts { } return undefined; } - function getPropertyAssignmentType(parentType: Type, property: PropertyAssignment | ShorthandPropertyAssignment) { - const name = property.name; + function getPropertyAssignmentType(parentType: Type, propertyAssignment: PropertyAssignment | ShorthandPropertyAssignment) { + const name = propertyAssignment.name; if (name.kind === SyntaxKind.Identifier) { const property = getPropertyOfType(parentType, (name).text); if (property) return getTypeOfSymbol(property); } - return undefined; + return unknownType; } function narrowTypeByAssignment(assignedType: Type) { // Narrow union types only From 46786b34486ba28e5264336cc18f8b7b9aba1023 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 21:20:08 +0100 Subject: [PATCH 36/49] Fix narrowing in for of statement --- src/compiler/binder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 494209439859a..1a936be5f4702 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -710,15 +710,15 @@ namespace ts { 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); - bindFlowMarker(n.initializer); - const preStatementState = currentReachabilityState; + const postStatementLabel = pushImplicitLabel(n); bind(n.statement); bindIterationFlowMarkerEnd(n.initializer); - popImplicitLabel(postStatementLabel, preStatementState, n.initializer); + popImplicitLabel(postStatementLabel, or(preInitializerState, preStatementState), n.initializer); } function bindIfStatement(n: IfStatement): void { From 3a7575b5d062ba9dfcf270db64ae41027007d7e9 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 21:20:31 +0100 Subject: [PATCH 37/49] Stop narrowing with guards that contain assignments --- src/compiler/checker.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9faf480c02083..ad348ca3b872c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7234,7 +7234,18 @@ namespace ts { } for (let i = guards.length - 1; i >= 0; i--) { const { expression, trueBranch } = guards[i]; - type = narrowType(type, expression, trueBranch); + const narrowed = narrowType(type, expression, trueBranch); + if (narrowed !== type && isVariableAssignedWithin(symbol, 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; From b9f5b79e86dbde6b14ee1fb36ce567a2061020d8 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 21:22:21 +0100 Subject: [PATCH 38/49] Add control flow tests --- .../controlFlowBinaryAndExpression.ts | 9 +++ .../controlFlowBinaryOrExpression.ts | 9 +++ .../controlFlowConditionalExpression.ts | 5 ++ .../controlFlowDoWhileStatement.ts | 76 +++++++++++++++++++ .../controlFlow/controlFlowForInStatement.ts | 17 +++++ .../controlFlow/controlFlowForOfStatement.ts | 10 +++ .../controlFlow/controlFlowForStatement.ts | 41 ++++++++++ .../controlFlow/controlFlowIfStatement.ts | 36 +++++++++ .../controlFlow/controlFlowWhileStatement.ts | 75 ++++++++++++++++++ 9 files changed, 278 insertions(+) create mode 100644 tests/cases/conformance/controlFlow/controlFlowBinaryAndExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowBinaryOrExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowConditionalExpression.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForInStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForOfStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowForStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowIfStatement.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts 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..275083a728beb --- /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; // number +} 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 +} From 21efd00212481b5a220fe4a0c78adaef76beb0c0 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 21:22:32 +0100 Subject: [PATCH 39/49] Add test for narrowing after assignment --- .../assignmentTypeNarrowing.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts 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 +} From d5fbd4e9fae32af5dea199c91401c4f7cc23d622 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Tue, 15 Mar 2016 21:22:54 +0100 Subject: [PATCH 40/49] Modify existing tests for new type guards behavior --- .../typeGuards/typeGuardsInIfStatement.ts | 45 +++++++++---------- .../stringLiteralTypesTypePredicates01.ts | 3 ++ .../unionTypesAssignability.ts | 20 +++++---- 3 files changed, 36 insertions(+), 32 deletions(-) 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/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 } From f8a1827225fc69d4922325a9699ee229b3c4ad8e Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 16 Mar 2016 09:15:02 +0100 Subject: [PATCH 41/49] Add more tests --- .../controlFlowAssignmentExpression.ts | 10 +++++++ .../typeGuards/typeGuardsInDoStatement.ts | 27 +++++++++++++++++++ .../typeGuards/typeGuardsInForStatement.ts | 21 +++++++++++++++ .../typeGuards/typeGuardsInWhileStatement.ts | 24 +++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts diff --git a/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts b/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts new file mode 100644 index 0000000000000..83bf75ab94d0c --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts @@ -0,0 +1,10 @@ +let x: string | boolean | number; +let obj: any; + +x = ""; +x = x.length; +x; // number + +x = true; +(x = "", obj).foo = (x = x.length); +x; // number diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts new file mode 100644 index 0000000000000..a1dd918a2fbed --- /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 +} +function b(x: string | number | boolean) { + x = true; + do { + x; // boolean | string + if (cond) continue; + x = undefined; + } while (typeof x === "string") + x; // number +} +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..20f2546e6b035 --- /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; // number | number +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts new file mode 100644 index 0000000000000..642c899579531 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInWhileStatement.ts @@ -0,0 +1,24 @@ +let cond: boolean; +function a(x: string | number) { + while (typeof x === "string") { + x; // string + x = undefined; + } + x; // number +} +function b(x: string | number) { + while (typeof x === "string") { + if (cond) continue; + x; // string + x = undefined; + } + x; // number +} +function c(x: string | number) { + while (typeof x === "string") { + if (cond) break; + x; // string + x = undefined; + } + x; // string | number +} From 7cceb0cdd67a30882e82257dd34d98ff0fa3585c Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 16 Mar 2016 09:25:27 +0100 Subject: [PATCH 42/49] Remove redundant bindFlowMarker calls --- src/compiler/binder.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1a936be5f4702..29606c3b9ecb2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -665,7 +665,6 @@ namespace ts { // bind expressions (don't affect reachability) bind(n.expression, true); - bindFlowMarker(n.expression); const postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? defaultUnreachable : currentReachabilityState; @@ -684,7 +683,6 @@ namespace ts { const loopStartNode = n.condition || n.statement; bind(n.condition, loopStartNode === n.condition); - if (loopStartNode === n.condition) bindFlowMarker(loopStartNode); const preForState = currentReachabilityState; const postForLabel = pushImplicitLabel(n); @@ -694,7 +692,6 @@ namespace ts { if (n.incrementor) { bind(n.incrementor, true); - bindFlowMarker(n.incrementor); } bindIterationFlowMarkerEnd(loopStartNode); From bea32e296c8ea9d246def0fafec5b15233530011 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 16 Mar 2016 10:14:23 +0100 Subject: [PATCH 43/49] Fix tests --- tests/cases/conformance/controlFlow/controlFlowIfStatement.ts | 2 +- .../expressions/typeGuards/typeGuardsInDoStatement.ts | 4 ++-- .../expressions/typeGuards/typeGuardsInForStatement.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts index 275083a728beb..c9e9be92f8e7e 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts @@ -32,5 +32,5 @@ function b() { else { x = ""; } - x; // number + x; // string } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts index a1dd918a2fbed..1afbe515df8d4 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInDoStatement.ts @@ -5,7 +5,7 @@ function a(x: string | number | boolean) { x; // boolean | string x = undefined; } while (typeof x === "string") - x; // number + x; // number | boolean } function b(x: string | number | boolean) { x = true; @@ -14,7 +14,7 @@ function b(x: string | number | boolean) { if (cond) continue; x = undefined; } while (typeof x === "string") - x; // number + x; // number | boolean } function c(x: string | number) { x = ""; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts index 20f2546e6b035..cf5c008e80050 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInForStatement.ts @@ -17,5 +17,5 @@ function c(x: string | number) { x; // string if (cond) break; } - x; // number | number + x; // string | number } From a6592395920617c3dfd5baf631cc21cc90640e65 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 16 Mar 2016 10:14:36 +0100 Subject: [PATCH 44/49] Accept baseline --- .../reference/TypeGuardWithEnumUnion.types | 8 +- .../reference/assignmentTypeNarrowing.js | 53 ++++ .../reference/assignmentTypeNarrowing.symbols | 64 +++++ .../reference/assignmentTypeNarrowing.types | 98 +++++++ .../controlFlowAssignmentExpression.js | 22 ++ .../controlFlowAssignmentExpression.symbols | 33 +++ .../controlFlowAssignmentExpression.types | 47 ++++ .../controlFlowBinaryAndExpression.js | 20 ++ .../controlFlowBinaryAndExpression.symbols | 24 ++ .../controlFlowBinaryAndExpression.types | 37 +++ .../controlFlowBinaryOrExpression.js | 20 ++ .../controlFlowBinaryOrExpression.symbols | 24 ++ .../controlFlowBinaryOrExpression.types | 37 +++ .../controlFlowConditionalExpression.js | 13 + .../controlFlowConditionalExpression.symbols | 15 ++ .../controlFlowConditionalExpression.types | 20 ++ .../reference/controlFlowDoWhileStatement.js | 157 +++++++++++ .../controlFlowDoWhileStatement.symbols | 181 +++++++++++++ .../controlFlowDoWhileStatement.types | 220 +++++++++++++++ .../reference/controlFlowForInStatement.js | 37 +++ .../controlFlowForInStatement.symbols | 43 +++ .../reference/controlFlowForInStatement.types | 50 ++++ .../reference/controlFlowForOfStatement.js | 24 ++ .../controlFlowForOfStatement.symbols | 28 ++ .../reference/controlFlowForOfStatement.types | 32 +++ .../reference/controlFlowForStatement.js | 87 ++++++ .../reference/controlFlowForStatement.symbols | 112 ++++++++ .../reference/controlFlowForStatement.types | 152 +++++++++++ .../reference/controlFlowIfStatement.js | 74 ++++++ .../reference/controlFlowIfStatement.symbols | 74 ++++++ .../reference/controlFlowIfStatement.types | 93 +++++++ .../reference/controlFlowWhileStatement.js | 155 +++++++++++ .../controlFlowWhileStatement.symbols | 177 ++++++++++++ .../reference/controlFlowWhileStatement.types | 216 +++++++++++++++ ...ationCollidingNamesInAugmentation1.symbols | 57 ---- ...ntationCollidingNamesInAugmentation1.types | 69 ----- .../reference/narrowTypeByInstanceof.types | 2 +- .../reference/reachabilityChecks5.errors.txt | 5 +- .../reference/reachabilityChecks6.errors.txt | 5 +- .../stringLiteralTypesInUnionTypes01.types | 44 +-- .../stringLiteralTypesInUnionTypes02.types | 38 +-- .../stringLiteralTypesInUnionTypes03.types | 18 +- .../stringLiteralTypesInUnionTypes04.types | 48 ++-- .../stringLiteralTypesTypePredicates01.js | 6 + ...stringLiteralTypesTypePredicates01.symbols | 16 +- .../stringLiteralTypesTypePredicates01.types | 11 +- .../baselines/reference/typeGuardEnums.types | 6 +- .../reference/typeGuardNesting.types | 10 +- .../typeGuardOfFormExpr1OrExpr2.types | 10 +- .../reference/typeGuardOfFormNotExpr.types | 8 +- .../typeGuardOfFormTypeOfBoolean.types | 2 +- .../typeGuardOfFormTypeOfNumber.types | 6 +- .../typeGuardOfFormTypeOfString.types | 8 +- .../reference/typeGuardRedundancy.types | 2 +- .../typeGuardsInConditionalExpression.symbols | 4 +- .../typeGuardsInConditionalExpression.types | 104 ++++---- .../reference/typeGuardsInDoStatement.js | 60 +++++ .../reference/typeGuardsInDoStatement.symbols | 74 ++++++ .../reference/typeGuardsInDoStatement.types | 92 +++++++ .../reference/typeGuardsInForStatement.js | 48 ++++ .../typeGuardsInForStatement.symbols | 62 +++++ .../reference/typeGuardsInForStatement.types | 77 ++++++ .../reference/typeGuardsInIfStatement.js | 90 +++---- .../reference/typeGuardsInIfStatement.symbols | 251 +++++++++--------- .../reference/typeGuardsInIfStatement.types | 125 ++++----- ...ardsInRightOperandOfAndAndOperator.symbols | 8 +- ...GuardsInRightOperandOfAndAndOperator.types | 38 +-- ...GuardsInRightOperandOfOrOrOperator.symbols | 8 +- ...peGuardsInRightOperandOfOrOrOperator.types | 42 +-- .../reference/typeGuardsInWhileStatement.js | 54 ++++ .../typeGuardsInWhileStatement.symbols | 62 +++++ .../typeGuardsInWhileStatement.types | 74 ++++++ .../unionTypesAssignability.errors.txt | 33 +-- .../reference/unionTypesAssignability.js | 20 +- 74 files changed, 3548 insertions(+), 596 deletions(-) create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.js create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.symbols create mode 100644 tests/baselines/reference/assignmentTypeNarrowing.types create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.js create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.symbols create mode 100644 tests/baselines/reference/controlFlowAssignmentExpression.types create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.js create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.symbols create mode 100644 tests/baselines/reference/controlFlowBinaryAndExpression.types create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.js create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.symbols create mode 100644 tests/baselines/reference/controlFlowBinaryOrExpression.types create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.js create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.symbols create mode 100644 tests/baselines/reference/controlFlowConditionalExpression.types create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.js create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.symbols create mode 100644 tests/baselines/reference/controlFlowDoWhileStatement.types create mode 100644 tests/baselines/reference/controlFlowForInStatement.js create mode 100644 tests/baselines/reference/controlFlowForInStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForInStatement.types create mode 100644 tests/baselines/reference/controlFlowForOfStatement.js create mode 100644 tests/baselines/reference/controlFlowForOfStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForOfStatement.types create mode 100644 tests/baselines/reference/controlFlowForStatement.js create mode 100644 tests/baselines/reference/controlFlowForStatement.symbols create mode 100644 tests/baselines/reference/controlFlowForStatement.types create mode 100644 tests/baselines/reference/controlFlowIfStatement.js create mode 100644 tests/baselines/reference/controlFlowIfStatement.symbols create mode 100644 tests/baselines/reference/controlFlowIfStatement.types create mode 100644 tests/baselines/reference/controlFlowWhileStatement.js create mode 100644 tests/baselines/reference/controlFlowWhileStatement.symbols create mode 100644 tests/baselines/reference/controlFlowWhileStatement.types delete mode 100644 tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.symbols delete mode 100644 tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.types create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.js create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInDoStatement.types create mode 100644 tests/baselines/reference/typeGuardsInForStatement.js create mode 100644 tests/baselines/reference/typeGuardsInForStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInForStatement.types create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.js create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.symbols create mode 100644 tests/baselines/reference/typeGuardsInWhileStatement.types 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/moduleAugmentationCollidingNamesInAugmentation1.symbols b/tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.symbols deleted file mode 100644 index ec933695900ad..0000000000000 --- a/tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.symbols +++ /dev/null @@ -1,57 +0,0 @@ -=== tests/cases/compiler/map1.ts === - -import { Observable } from "./observable" ->Observable : Symbol(Observable, Decl(map1.ts, 1, 8)) - -(Observable.prototype).map = function() { } ->Observable.prototype : Symbol(Observable.prototype) ->Observable : Symbol(Observable, Decl(map1.ts, 1, 8)) ->prototype : Symbol(Observable.prototype) - -declare module "./observable" { - interface I {x0} ->I : Symbol(I, Decl(map1.ts, 5, 31), Decl(map2.ts, 4, 31)) ->x0 : Symbol(x0, Decl(map1.ts, 6, 17)) -} - -=== tests/cases/compiler/map2.ts === -import { Observable } from "./observable" ->Observable : Symbol(Observable, Decl(map2.ts, 0, 8)) - -(Observable.prototype).map = function() { } ->Observable.prototype : Symbol(Observable.prototype) ->Observable : Symbol(Observable, Decl(map2.ts, 0, 8)) ->prototype : Symbol(Observable.prototype) - -declare module "./observable" { - interface I {x1} ->I : Symbol(I, Decl(map1.ts, 5, 31), Decl(map2.ts, 4, 31)) ->x1 : Symbol(x1, Decl(map2.ts, 5, 17)) -} - - -=== tests/cases/compiler/observable.ts === -export declare class Observable { ->Observable : Symbol(Observable, Decl(observable.ts, 0, 0)) ->T : Symbol(T, Decl(observable.ts, 0, 32)) - - filter(pred: (e:T) => boolean): Observable; ->filter : Symbol(filter, Decl(observable.ts, 0, 36)) ->pred : Symbol(pred, Decl(observable.ts, 1, 11)) ->e : Symbol(e, Decl(observable.ts, 1, 18)) ->T : Symbol(T, Decl(observable.ts, 0, 32)) ->Observable : Symbol(Observable, Decl(observable.ts, 0, 0)) ->T : Symbol(T, Decl(observable.ts, 0, 32)) -} - -=== tests/cases/compiler/main.ts === -import { Observable } from "./observable" ->Observable : Symbol(Observable, Decl(main.ts, 0, 8)) - -import "./map1"; -import "./map2"; - -let x: Observable; ->x : Symbol(x, Decl(main.ts, 4, 3)) ->Observable : Symbol(Observable, Decl(main.ts, 0, 8)) - diff --git a/tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.types b/tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.types deleted file mode 100644 index e87560c6a3d1e..0000000000000 --- a/tests/baselines/reference/moduleAugmentationCollidingNamesInAugmentation1.types +++ /dev/null @@ -1,69 +0,0 @@ -=== tests/cases/compiler/map1.ts === - -import { Observable } from "./observable" ->Observable : typeof Observable - -(Observable.prototype).map = function() { } ->(Observable.prototype).map = function() { } : () => void ->(Observable.prototype).map : any ->(Observable.prototype) : any ->Observable.prototype : any ->Observable.prototype : Observable ->Observable : typeof Observable ->prototype : Observable ->map : any ->function() { } : () => void - -declare module "./observable" { - interface I {x0} ->I : I ->x0 : any -} - -=== tests/cases/compiler/map2.ts === -import { Observable } from "./observable" ->Observable : typeof Observable - -(Observable.prototype).map = function() { } ->(Observable.prototype).map = function() { } : () => void ->(Observable.prototype).map : any ->(Observable.prototype) : any ->Observable.prototype : any ->Observable.prototype : Observable ->Observable : typeof Observable ->prototype : Observable ->map : any ->function() { } : () => void - -declare module "./observable" { - interface I {x1} ->I : I ->x1 : any -} - - -=== tests/cases/compiler/observable.ts === -export declare class Observable { ->Observable : Observable ->T : T - - filter(pred: (e:T) => boolean): Observable; ->filter : (pred: (e: T) => boolean) => Observable ->pred : (e: T) => boolean ->e : T ->T : T ->Observable : Observable ->T : T -} - -=== tests/cases/compiler/main.ts === -import { Observable } from "./observable" ->Observable : typeof Observable - -import "./map1"; -import "./map2"; - -let x: Observable; ->x : Observable ->Observable : Observable - 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/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/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/typeGuardsInConditionalExpression.symbols b/tests/baselines/reference/typeGuardsInConditionalExpression.symbols index 7de63d90bf92d..76c3faf84b796 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.symbols +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.symbols @@ -234,9 +234,9 @@ function foo12(x: number | string | boolean) { ? (x = 10 && x.toString().length) // number | boolean | string - changed here >x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 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.toString : Symbol(String.toString, 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, --, --)) +>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 diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.types b/tests/baselines/reference/typeGuardsInConditionalExpression.types index 493a8c0a31afd..99141c76b8bb1 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.types +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -27,126 +27,126 @@ 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 | number : 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 : number | string ->10 && x : number | string +>10 && x : string >10 : number ->x : number | string +>x : string : x; // string | number ->x : number | string +>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 | number : 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 : number | string ->"Hello" && x : number | string +>"Hello" && x : string >"Hello" : string ->x : number | string +>x : string : x; // string | number ->x : number | string +>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 | number : (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 = 10 && x); // string | number ->(x = 10 && x) : number | string ->x = 10 && x : number | string +>(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 | number : (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 = "hello" && x); // string | number ->(x = "hello" && x) : number | string ->x = "hello" && x : number | string +>(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 | number : (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 : 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 : 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 @@ -296,7 +296,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 +305,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 // 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) : 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 : ((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 @@ -363,24 +363,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) : 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..878d636c87ca9 --- /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..23edf6a14fabd --- /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; // number | boolean +>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..b723091baa5ae --- /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; // number | boolean +>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/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..7f89c75da5fb1 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(Object.toString, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) +>toString : Symbol(Object.toString, 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)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) +>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) } } diff --git a/tests/baselines/reference/typeGuardsInIfStatement.types b/tests/baselines/reference/typeGuardsInIfStatement.types index 0095a7cc768ea..009d3accf187f 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) { @@ -294,7 +299,7 @@ function foo10(x: number | 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 +>x : number | 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 : boolean >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : 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,14 +385,14 @@ 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 >typeof x === "number" : boolean >typeof x : string ->x : number | string | boolean +>x : number >"number" : string ? x.toString() // number @@ -398,8 +403,8 @@ function foo12(x: number | string | boolean) { : x.toString(); // boolean | string >x.toString() : string ->x.toString : () => string ->x : string | boolean ->toString : () => string +>x.toString : (radix?: number) => string +>x : number | string | boolean +>toString : (radix?: number) => 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 } From 34909799e8afb880940db70d618432dd92d415a7 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 16 Mar 2016 13:32:39 +0100 Subject: [PATCH 45/49] Update tests --- .../reference/assignmentCompatability3.js | 25 --- .../assignmentCompatability3.symbols | 39 ---- .../reference/assignmentCompatability3.types | 44 ---- .../typeGuardsInConditionalExpression.js | 66 +++--- .../typeGuardsInConditionalExpression.symbols | 192 +++++++++--------- .../typeGuardsInConditionalExpression.types | 60 +++--- .../reference/typeGuardsInDoStatement.js | 54 ++--- .../reference/typeGuardsInDoStatement.symbols | 2 +- .../reference/typeGuardsInDoStatement.types | 2 +- .../typeGuardsInConditionalExpression.ts | 34 ++-- 10 files changed, 190 insertions(+), 328 deletions(-) delete mode 100644 tests/baselines/reference/assignmentCompatability3.js delete mode 100644 tests/baselines/reference/assignmentCompatability3.symbols delete mode 100644 tests/baselines/reference/assignmentCompatability3.types diff --git a/tests/baselines/reference/assignmentCompatability3.js b/tests/baselines/reference/assignmentCompatability3.js deleted file mode 100644 index 7120e6ce3e24b..0000000000000 --- a/tests/baselines/reference/assignmentCompatability3.js +++ /dev/null @@ -1,25 +0,0 @@ -//// [assignmentCompatability3.ts] -module __test1__ { - export interface interfaceWithPublicAndOptional { one: T; two?: U; }; var obj4: interfaceWithPublicAndOptional = { one: 1 };; - export var __val__obj4 = obj4; -} -module __test2__ { - export var obj = {one: 1}; - export var __val__obj = obj; -} -__test2__.__val__obj = __test1__.__val__obj4 - -//// [assignmentCompatability3.js] -var __test1__; -(function (__test1__) { - ; - var obj4 = { one: 1 }; - ; - __test1__.__val__obj4 = obj4; -})(__test1__ || (__test1__ = {})); -var __test2__; -(function (__test2__) { - __test2__.obj = { one: 1 }; - __test2__.__val__obj = __test2__.obj; -})(__test2__ || (__test2__ = {})); -__test2__.__val__obj = __test1__.__val__obj4; diff --git a/tests/baselines/reference/assignmentCompatability3.symbols b/tests/baselines/reference/assignmentCompatability3.symbols deleted file mode 100644 index c31d69e2f1287..0000000000000 --- a/tests/baselines/reference/assignmentCompatability3.symbols +++ /dev/null @@ -1,39 +0,0 @@ -=== tests/cases/compiler/assignmentCompatability3.ts === -module __test1__ { ->__test1__ : Symbol(__test1__, Decl(assignmentCompatability3.ts, 0, 0)) - - export interface interfaceWithPublicAndOptional { one: T; two?: U; }; var obj4: interfaceWithPublicAndOptional = { one: 1 };; ->interfaceWithPublicAndOptional : Symbol(interfaceWithPublicAndOptional, Decl(assignmentCompatability3.ts, 0, 18)) ->T : Symbol(T, Decl(assignmentCompatability3.ts, 1, 52)) ->U : Symbol(U, Decl(assignmentCompatability3.ts, 1, 54)) ->one : Symbol(one, Decl(assignmentCompatability3.ts, 1, 58)) ->T : Symbol(T, Decl(assignmentCompatability3.ts, 1, 52)) ->two : Symbol(two, Decl(assignmentCompatability3.ts, 1, 66)) ->U : Symbol(U, Decl(assignmentCompatability3.ts, 1, 54)) ->obj4 : Symbol(obj4, Decl(assignmentCompatability3.ts, 1, 83)) ->interfaceWithPublicAndOptional : Symbol(interfaceWithPublicAndOptional, Decl(assignmentCompatability3.ts, 0, 18)) ->one : Symbol(one, Decl(assignmentCompatability3.ts, 1, 139)) - - export var __val__obj4 = obj4; ->__val__obj4 : Symbol(__val__obj4, Decl(assignmentCompatability3.ts, 2, 14)) ->obj4 : Symbol(obj4, Decl(assignmentCompatability3.ts, 1, 83)) -} -module __test2__ { ->__test2__ : Symbol(__test2__, Decl(assignmentCompatability3.ts, 3, 1)) - - export var obj = {one: 1}; ->obj : Symbol(obj, Decl(assignmentCompatability3.ts, 5, 14)) ->one : Symbol(one, Decl(assignmentCompatability3.ts, 5, 22)) - - export var __val__obj = obj; ->__val__obj : Symbol(__val__obj, Decl(assignmentCompatability3.ts, 6, 14)) ->obj : Symbol(obj, Decl(assignmentCompatability3.ts, 5, 14)) -} -__test2__.__val__obj = __test1__.__val__obj4 ->__test2__.__val__obj : Symbol(__test2__.__val__obj, Decl(assignmentCompatability3.ts, 6, 14)) ->__test2__ : Symbol(__test2__, Decl(assignmentCompatability3.ts, 3, 1)) ->__val__obj : Symbol(__test2__.__val__obj, Decl(assignmentCompatability3.ts, 6, 14)) ->__test1__.__val__obj4 : Symbol(__test1__.__val__obj4, Decl(assignmentCompatability3.ts, 2, 14)) ->__test1__ : Symbol(__test1__, Decl(assignmentCompatability3.ts, 0, 0)) ->__val__obj4 : Symbol(__test1__.__val__obj4, Decl(assignmentCompatability3.ts, 2, 14)) - diff --git a/tests/baselines/reference/assignmentCompatability3.types b/tests/baselines/reference/assignmentCompatability3.types deleted file mode 100644 index 67e553235edcb..0000000000000 --- a/tests/baselines/reference/assignmentCompatability3.types +++ /dev/null @@ -1,44 +0,0 @@ -=== tests/cases/compiler/assignmentCompatability3.ts === -module __test1__ { ->__test1__ : typeof __test1__ - - export interface interfaceWithPublicAndOptional { one: T; two?: U; }; var obj4: interfaceWithPublicAndOptional = { one: 1 };; ->interfaceWithPublicAndOptional : interfaceWithPublicAndOptional ->T : T ->U : U ->one : T ->T : T ->two : U ->U : U ->obj4 : interfaceWithPublicAndOptional ->interfaceWithPublicAndOptional : interfaceWithPublicAndOptional ->{ one: 1 } : { one: number; } ->one : number ->1 : number - - export var __val__obj4 = obj4; ->__val__obj4 : interfaceWithPublicAndOptional ->obj4 : interfaceWithPublicAndOptional -} -module __test2__ { ->__test2__ : typeof __test2__ - - export var obj = {one: 1}; ->obj : { one: number; } ->{one: 1} : { one: number; } ->one : number ->1 : number - - export var __val__obj = obj; ->__val__obj : { one: number; } ->obj : { one: number; } -} -__test2__.__val__obj = __test1__.__val__obj4 ->__test2__.__val__obj = __test1__.__val__obj4 : __test1__.interfaceWithPublicAndOptional ->__test2__.__val__obj : { one: number; } ->__test2__ : typeof __test2__ ->__val__obj : { one: number; } ->__test1__.__val__obj4 : __test1__.interfaceWithPublicAndOptional ->__test1__ : typeof __test1__ ->__val__obj4 : __test1__.interfaceWithPublicAndOptional - 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 76c3faf84b796..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(String.toString, Decl(lib.d.ts, --, --)) ->x : Symbol(x, Decl(typeGuardsInConditionalExpression.ts, 87, 15)) +>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 99141c76b8bb1..456b7a510456c 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.types +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -30,15 +30,14 @@ function foo2(x: 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 : string | number +>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) // string >(x = 10 && x) : string >x = 10 && x : string >x : number | string @@ -46,23 +45,21 @@ function foo2(x: number | string) { >10 : number >x : string - : x; // string | number + : x; // number >x : number } function foo3(x: 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 : string | number +>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) // string >(x = "Hello" && x) : string >x = "Hello" && x : string >x : number | string @@ -70,26 +67,24 @@ function foo3(x: number | string) { >"Hello" : string >x : string - : x; // string | number + : x; // number >x : number } function foo4(x: 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) : string | number +>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 // string >x : string - : (x = 10 && x); // string | number + : (x = 10 && x); // number >(x = 10 && x) : number >x = 10 && x : number >x : number | string @@ -101,18 +96,17 @@ function foo5(x: 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) : string | number +>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 // string >x : string - : (x = "hello" && x); // string | number + : (x = "hello" && x); // number >(x = "hello" && x) : number >x = "hello" && x : number >x : number | string @@ -126,13 +120,13 @@ function foo6(x: number | string) { // Modify in both branches return typeof x === "string" ->typeof x === "string" ? (x = 10 && x) // string | number : (x = "hello" && x) : string | number +>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) // string >(x = 10 && x) : string >x = 10 && x : string >x : number | string @@ -140,7 +134,7 @@ function foo6(x: number | string) { >10 : number >x : string - : (x = "hello" && x); // string | number + : (x = "hello" && x); // number >(x = "hello" && x) : number >x = "hello" && x : number >x : number | string @@ -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 @@ -305,13 +299,13 @@ 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) : string | number | 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 // string >x : string : ((b = x) // x is number | boolean | string - because the assignment changed it @@ -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 @@ -368,10 +362,10 @@ function foo12(x: number | string | boolean) { >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) // 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 diff --git a/tests/baselines/reference/typeGuardsInDoStatement.js b/tests/baselines/reference/typeGuardsInDoStatement.js index 878d636c87ca9..7d299854f7d25 100644 --- a/tests/baselines/reference/typeGuardsInDoStatement.js +++ b/tests/baselines/reference/typeGuardsInDoStatement.js @@ -1,31 +1,31 @@ //// [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 -} +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] diff --git a/tests/baselines/reference/typeGuardsInDoStatement.symbols b/tests/baselines/reference/typeGuardsInDoStatement.symbols index 23edf6a14fabd..7dff18d8d238f 100644 --- a/tests/baselines/reference/typeGuardsInDoStatement.symbols +++ b/tests/baselines/reference/typeGuardsInDoStatement.symbols @@ -68,7 +68,7 @@ function c(x: string | number) { } while (typeof x === "string") >x : Symbol(x, Decl(typeGuardsInDoStatement.ts, 18, 11)) - x; // number | boolean + 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 index b723091baa5ae..79183e7d6c8d4 100644 --- a/tests/baselines/reference/typeGuardsInDoStatement.types +++ b/tests/baselines/reference/typeGuardsInDoStatement.types @@ -86,7 +86,7 @@ function c(x: string | number) { >x : string | number >"string" : string - x; // number | boolean + x; // string | number >x : string | number } 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 From 90a678d674961d5f6f95ed3b6f2686551eb7a6f2 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 23 Mar 2016 19:44:21 +0100 Subject: [PATCH 46/49] Implement flow based type guards for this expression and dotted names --- src/compiler/binder.ts | 10 ++ src/compiler/checker.ts | 314 ++++++++++++++++++---------------------- src/compiler/types.ts | 4 +- 3 files changed, 153 insertions(+), 175 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 29606c3b9ecb2..df4947b69cd37 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1376,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 bindIdentifier(node); + case SyntaxKind.PropertyAccessExpression: + return bindPropertyAccessExpression(node); case SyntaxKind.BinaryExpression: if (isInJavaScriptFile(node)) { const specialKind = getSpecialPropertyAssignmentKind(node); @@ -1799,6 +1803,9 @@ namespace ts { : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } + function bindThisExpression(node: Node) { + bindFlowMarker(node); + } function bindIdentifier(node: Identifier) { checkStrictModeIdentifier(node); if (isExpression(node) @@ -1811,6 +1818,9 @@ namespace ts { bindFlowMarker(node); } } + function bindPropertyAccessExpression(node: PropertyAccessExpression) { + bindFlowMarker(node); + } function bindFlowMarker(node: Node) { currentReachabilityState = { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 215c8aef0088e..963f4fd3c2186 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7103,6 +7103,7 @@ namespace ts { result.resolvedSymbol = symbol; result.parent = location; result.id = -1; + result.previous = [location]; return result; } @@ -7299,7 +7300,7 @@ namespace ts { return false; } - function getPreviousOccurences(symbol: Symbol, where: Node, callback: (node: Node, guards: BranchFlow[]) => boolean) { + 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[] = []; @@ -7323,14 +7324,22 @@ namespace ts { nodeId = getNodeId(location); if (visited[nodeId]) continue; - let isSameIdentifier = false; - if ((location).kind === SyntaxKind.Identifier) { - const identifier = location; - const otherSymbol = resolveName(identifier, identifier.text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); - isSameIdentifier = identifier !== where && (identifier).kind === SyntaxKind.Identifier && otherSymbol && otherSymbol.id === symbol.id; + const same = isSameSymbol(reference, location); + let previousLocation = location; + if (same && reference.kind === SyntaxKind.PropertyAccessExpression) { + let node = (location).parent; + while (node.kind === SyntaxKind.PropertyAccessExpression) { + if (isLeftHandSideOfAssignment(node)) { + // Reference node was part of an assignment + // Example: a.b.c is reference, a or a.b was assigned + previousLocation = undefined; + break; + } + node = node.parent; + } } - if (isSameIdentifier || isBranchStart(location)) { - if (callback( location, guards)) { + if (same || isBranchStart(location)) { + if (callback(previousLocation, guards, same)) { stop = true; break; } @@ -7347,7 +7356,7 @@ namespace ts { } if (location.previous === undefined) { // We cannot do analysis in a catch or finally block - if (callback(undefined, guards)) { + if (callback(undefined, guards, true)) { stop = true; break; } @@ -7365,6 +7374,25 @@ namespace ts { 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.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; @@ -7373,51 +7401,77 @@ namespace ts { } // Get the narrowed type of a given symbol at a given location - function getNarrowedTypeOfSymbol(symbol: Symbol, location: Node) { - const initialType = getTypeOfSymbol(symbol); + 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 initialType; + } + let top: Node; + const isDottedName = leftmostNode !== reference; + let thisKeyword: Node; + let leftmostSymbol: Symbol; + if (leftmostNode.kind === SyntaxKind.Identifier) { + leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode)); + if (!leftmostSymbol) { + return initialType; + } + const declaration = leftmostSymbol.valueDeclaration; + if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) { + return initialType; + } + top = getDeclarationContainer(declaration); - // Only narrow when symbol is variable of type any or an object, union, or type parameter type - if (!location || !(symbol.flags & SymbolFlags.Variable)) return initialType; - if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; - - if (isLeftHandSideOfAssignment(location)) return initialType; + // Only narrow when symbol is variable of type any or an object, union, or type parameter type + if (!(leftmostSymbol.flags & SymbolFlags.Variable)) return initialType; - let saveLocalType = false; - let nodeLinks: NodeLinks; - if (location.kind === SyntaxKind.Identifier) { - const locationSymbol = resolveName(location, (location).text, SymbolFlags.Value | SymbolFlags.ExportValue, undefined, undefined); - if (locationSymbol && locationSymbol.id === symbol.id) { - nodeLinks = getNodeLinks(location); - if (nodeLinks.identifierNarrowingState === NarrowingState.Done) return nodeLinks.identifierLocalType; - if (nodeLinks.identifierNarrowingState === NarrowingState.Narrowing) { - nodeLinks.identifierNarrowingState = NarrowingState.Failed; - return nodeLinks.identifierLocalType = initialType; - } - saveLocalType = true; - nodeLinks.identifierNarrowingState = NarrowingState.Narrowing; + if (!leftmostSymbol.declarations) return initialType; + for (const declaration of leftmostSymbol.declarations) { + if (getSourceFileOfNode(declaration) !== getSourceFileOfNode(reference)) return initialType; } } - if (!symbol.declarations) return initialType; - for (const declaration of symbol.declarations) { - if (getSourceFileOfNode(declaration) !== getSourceFileOfNode(location)) return initialType; + else { + // leftmostNode is `this` keyword + if (!isDottedName) { + thisKeyword = reference; + } + } + + const symbol = isDottedName ? undefined : leftmostSymbol; + const isThisExpression = thisKeyword !== undefined; + const isIdentifier = !isDottedName && !isThisExpression; + + if (!isTypeAny(initialType) && !(initialType.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter))) return initialType; + + if (isLeftHandSideOfAssignment(reference)) { + return initialType; + } + + 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; + const visited: FlowMarkerTarget[] = []; const cache: { [nodeId: number]: Type } = {}; let loop = false; - const narrowedType = getType(location, false); - if (saveLocalType) { - if (nodeLinks.identifierNarrowingState === NarrowingState.Failed) { - // During recursion, the narrowing of this node failed. - return initialType; - } - nodeLinks.identifierNarrowingState = NarrowingState.Done; - nodeLinks.identifierLocalType = narrowedType; + const narrowedType = getType(reference, false, true); + if (nodeLinks.narrowingState === NarrowingState.Failed) { + // During recursion, the narrowing of this node failed. + return initialType; } - return narrowedType; + nodeLinks.narrowingState = NarrowingState.Done; + return nodeLinks.localType = narrowedType; - function getType(where: Node, after: boolean) { + function getType(where: Node, after: boolean, isSameSymbol: boolean) { if (where === undefined) return initialType; const whereId = getNodeId(where); if (after && cache[whereId]) { @@ -7427,45 +7481,44 @@ namespace ts { loop = true; return undefined; } - const isIdentifier = where.kind === SyntaxKind.Identifier; - let identifierNodeLinks: NodeLinks; - if (isIdentifier) { + let nodeLinks: NodeLinks; + if (isSameSymbol) { if (after) { - const assignment = getAssignedTypeAtLocation(where); + const assignment = getAssignedTypeAtLocation(where); if (assignment) { return cache[whereId] = narrowTypeByAssignment(assignment); } } - identifierNodeLinks = getNodeLinks(where); - if (identifierNodeLinks.identifierNarrowingState === NarrowingState.Done) { - return identifierNodeLinks.identifierLocalType; + nodeLinks = getNodeLinks(where); + if (nodeLinks.narrowingState === NarrowingState.Done) { + return nodeLinks.localType; } } let types: Type[] = []; visited.push(where); - const fallback = getPreviousOccurences(symbol, where, handleGuards); + const fallback = getPreviousOccurences(reference, where, handleGuards); visited.pop(); const type = fallback || types.length === 0 ? initialType : getUnionType(types); - if (!loop && isIdentifier && identifierNodeLinks.identifierNarrowingState !== NarrowingState.Failed) { - identifierNodeLinks.identifierLocalType = type; + if (!loop && isSameSymbol && nodeLinks.narrowingState !== NarrowingState.Failed) { + nodeLinks.localType = type; } if (after) { cache[whereId] = type; } return type; - function handleGuards(node: Identifier, guards: BranchFlow[]) { + function handleGuards(node: Node, guards: BranchFlow[], isSameSymbol: boolean) { if (!node && guards.length === 0) { return true; } - let type = getType(node, 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 && isVariableAssignedWithin(symbol, expression)) { + 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: @@ -7486,121 +7539,6 @@ namespace ts { return false; } } - - 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; - } - } - - // 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; - } - const leftmostNode = getLeftmostIdentifierOrThis(reference); - if (!leftmostNode) { - return type; - } - let top: Node; - if (leftmostNode.kind === SyntaxKind.Identifier) { - const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode)); - if (!leftmostSymbol) { - return type; - } - const declaration = leftmostSymbol.valueDeclaration; - if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) { - return type; - } - top = getDeclarationContainer(declaration); - } - const originalType = type; - const nodeStack: { node: Node, child: Node }[] = []; - let node: Node = reference; - loop: while (node.parent) { - const child = node; - node = node.parent; - switch (node.kind) { - case SyntaxKind.IfStatement: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.BinaryExpression: - nodeStack.push({node, child}); - break; - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleDeclaration: - break loop; - default: - if (node === top || isFunctionLikeKind(node.kind)) { - break loop; - } - break; - } - } - - 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!"); - } - - // Use original type if construct contains assignments to variable - if (type !== originalType && isAnyPartOfReferenceAssignedWithin(reference, node)) { - type = originalType; - } - } - - // Preserve old top-level behavior - if the branch is really an empty set, revert to prior type - if (type === emptyUnionType) { - type = originalType; - } - - return type; function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { return strictNullChecks && assumeTrue && isMatchingReference(expr, reference) ? getNonNullableType(type) : type; @@ -7839,6 +7777,8 @@ 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. @@ -7849,7 +7789,8 @@ namespace ts { // 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 { + } + else { return checkExpressionCached((parent).initializer); } } @@ -7930,6 +7871,7 @@ namespace ts { 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); @@ -7938,8 +7880,8 @@ namespace ts { return unknownType; } function narrowTypeByAssignment(assignedType: Type) { - // Narrow union types only - if (!isUnion || assignedType === anyType || assignedType === unknownType) return initialType; + if (!isUnion) return initialType; + if (assignedType === anyType || assignedType === unknownType) return initialType; const assignedTypes = (assignedType.flags & TypeFlags.Union) ? ( assignedType).types : [assignedType]; @@ -7983,6 +7925,32 @@ namespace ts { return getNarrowedTypeOfReference(type, createTransientIdentifier(symbol, location)); } + 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 { while (expression.kind === SyntaxKind.ParenthesizedExpression) { expression = (expression as ParenthesizedExpression).expression; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b9c34bb7058ac..85e6a0e38ee68 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2113,8 +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 - identifierNarrowingState?: NarrowingState; - identifierLocalType?: Type; + narrowingState?: NarrowingState; + localType?: Type; } export const enum TypeFlags { From 7646af1c7bd984e60ff60f37f7495cf06867a86d Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 23 Mar 2016 21:09:39 +0100 Subject: [PATCH 47/49] Fix unresolved symbols, wrong caching and empty type issues --- src/compiler/checker.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 963f4fd3c2186..e77539ed743c3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7106,6 +7106,9 @@ namespace ts { result.previous = [location]; return result; } + function cleanTransientIdentifier() { + nodeLinks[-1] = undefined; + } function getResolvedSymbol(node: Identifier): Symbol { if (node.id === -1) { @@ -7380,7 +7383,7 @@ namespace ts { 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.id === rightSymbol.id; + return leftSymbol !== undefined && rightSymbol !== undefined && leftSymbol.id === rightSymbol.id; } if (left.kind === SyntaxKind.ThisKeyword) { return getThisContainer(left, false) === getThisContainer(right, false); @@ -7463,11 +7466,12 @@ namespace ts { const cache: { [nodeId: number]: Type } = {}; let loop = false; - const narrowedType = getType(reference, false, true); + 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; @@ -7922,7 +7926,9 @@ 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 { From 4a76fe2989409307518613f3023bd8b202d4f5f8 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 23 Mar 2016 21:14:53 +0100 Subject: [PATCH 48/49] Accept new baselines --- .../reference/assignmentCompatability3.js | 25 +++++ .../reference/assignmentCompatability3.types | 44 ++++++++ ...gumentsWithStringLiteralTypes01.errors.txt | 7 +- .../reference/typeGuardInClass.errors.txt | 30 ----- .../reference/typeGuardInClass.symbols | 29 +++++ .../reference/typeGuardInClass.types | 34 ++++++ .../reference/typeGuardsDefeat.errors.txt | 52 --------- .../reference/typeGuardsDefeat.symbols | 82 ++++++++++++++ .../reference/typeGuardsDefeat.types | 105 ++++++++++++++++++ .../typeGuardsInFunctionAndModuleBlock.types | 20 ++-- .../reference/typeGuardsInIfStatement.symbols | 8 +- .../reference/typeGuardsInIfStatement.types | 26 ++--- 12 files changed, 347 insertions(+), 115 deletions(-) create mode 100644 tests/baselines/reference/assignmentCompatability3.js create mode 100644 tests/baselines/reference/assignmentCompatability3.types delete mode 100644 tests/baselines/reference/typeGuardInClass.errors.txt create mode 100644 tests/baselines/reference/typeGuardInClass.symbols create mode 100644 tests/baselines/reference/typeGuardInClass.types delete mode 100644 tests/baselines/reference/typeGuardsDefeat.errors.txt create mode 100644 tests/baselines/reference/typeGuardsDefeat.symbols create mode 100644 tests/baselines/reference/typeGuardsDefeat.types diff --git a/tests/baselines/reference/assignmentCompatability3.js b/tests/baselines/reference/assignmentCompatability3.js new file mode 100644 index 0000000000000..7120e6ce3e24b --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability3.js @@ -0,0 +1,25 @@ +//// [assignmentCompatability3.ts] +module __test1__ { + export interface interfaceWithPublicAndOptional { one: T; two?: U; }; var obj4: interfaceWithPublicAndOptional = { one: 1 };; + export var __val__obj4 = obj4; +} +module __test2__ { + export var obj = {one: 1}; + export var __val__obj = obj; +} +__test2__.__val__obj = __test1__.__val__obj4 + +//// [assignmentCompatability3.js] +var __test1__; +(function (__test1__) { + ; + var obj4 = { one: 1 }; + ; + __test1__.__val__obj4 = obj4; +})(__test1__ || (__test1__ = {})); +var __test2__; +(function (__test2__) { + __test2__.obj = { one: 1 }; + __test2__.__val__obj = __test2__.obj; +})(__test2__ || (__test2__ = {})); +__test2__.__val__obj = __test1__.__val__obj4; diff --git a/tests/baselines/reference/assignmentCompatability3.types b/tests/baselines/reference/assignmentCompatability3.types new file mode 100644 index 0000000000000..67e553235edcb --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability3.types @@ -0,0 +1,44 @@ +=== tests/cases/compiler/assignmentCompatability3.ts === +module __test1__ { +>__test1__ : typeof __test1__ + + export interface interfaceWithPublicAndOptional { one: T; two?: U; }; var obj4: interfaceWithPublicAndOptional = { one: 1 };; +>interfaceWithPublicAndOptional : interfaceWithPublicAndOptional +>T : T +>U : U +>one : T +>T : T +>two : U +>U : U +>obj4 : interfaceWithPublicAndOptional +>interfaceWithPublicAndOptional : interfaceWithPublicAndOptional +>{ one: 1 } : { one: number; } +>one : number +>1 : number + + export var __val__obj4 = obj4; +>__val__obj4 : interfaceWithPublicAndOptional +>obj4 : interfaceWithPublicAndOptional +} +module __test2__ { +>__test2__ : typeof __test2__ + + export var obj = {one: 1}; +>obj : { one: number; } +>{one: 1} : { one: number; } +>one : number +>1 : number + + export var __val__obj = obj; +>__val__obj : { one: number; } +>obj : { one: number; } +} +__test2__.__val__obj = __test1__.__val__obj4 +>__test2__.__val__obj = __test1__.__val__obj4 : __test1__.interfaceWithPublicAndOptional +>__test2__.__val__obj : { one: number; } +>__test2__ : typeof __test2__ +>__val__obj : { one: number; } +>__test1__.__val__obj4 : __test1__.interfaceWithPublicAndOptional +>__test1__ : typeof __test1__ +>__val__obj4 : __test1__.interfaceWithPublicAndOptional + 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/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/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/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.symbols b/tests/baselines/reference/typeGuardsInIfStatement.symbols index 7f89c75da5fb1..53e9fb5126f0b 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.symbols +++ b/tests/baselines/reference/typeGuardsInIfStatement.symbols @@ -264,9 +264,9 @@ function foo11(x: number | string | boolean) { 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(Object.toString, Decl(lib.d.ts, --, --)) +>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 113, 15)) ->toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) +>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) ); } @@ -302,8 +302,8 @@ function foo12(x: number | string | boolean) { >toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) : x.toString(); // boolean | string ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInIfStatement.ts, 133, 15)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>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 009d3accf187f..8992e71f53c44 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.types +++ b/tests/baselines/reference/typeGuardsInIfStatement.types @@ -183,7 +183,7 @@ function foo7(x: number | string | boolean) { else { return x == 10; // number >x == 10 : boolean ->x : number +>x : number | string >10 : number } } @@ -219,7 +219,7 @@ function foo8(x: number | string | boolean) { else { return x == 10; // number >x == 10 : boolean ->x : number +>x : number | string >10 : number } } @@ -259,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 @@ -283,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 @@ -295,7 +295,7 @@ function foo10(x: number | string | boolean) { >10 : number : x; // x should be boolean ->x : boolean +>x : string | boolean } } function foo11(x: number | string | boolean) { @@ -325,7 +325,7 @@ function foo11(x: number | string | boolean) { >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 | boolean +>x : number | string | boolean >"number" : string ? ( @@ -351,10 +351,10 @@ function foo11(x: number | string | boolean) { >y = x && x.toString() : string >y : number | boolean | string >x && x.toString() : string ->x : boolean +>x : string | boolean >x.toString() : string >x.toString : () => string ->x : boolean +>x : string | boolean >toString : () => string ); @@ -392,7 +392,7 @@ function foo12(x: number | string | boolean) { >typeof x === "number" ? x.toString() // number : x.toString() : string >typeof x === "number" : boolean >typeof x : string ->x : number +>x : number | string | boolean >"number" : string ? x.toString() // number @@ -403,8 +403,8 @@ function foo12(x: number | string | boolean) { : x.toString(); // boolean | string >x.toString() : string ->x.toString : (radix?: number) => string ->x : number | string | boolean ->toString : (radix?: number) => string +>x.toString : () => string +>x : string | boolean +>toString : () => string } } From 16555dd6ea27cf323c0cc3cf9bd680d65244f7f6 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Wed, 23 Mar 2016 22:19:46 +0100 Subject: [PATCH 49/49] Stop narrowing of dotted name after assignment of parent type --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e77539ed743c3..f9f4dbe056bbe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7327,18 +7327,19 @@ namespace ts { nodeId = getNodeId(location); if (visited[nodeId]) continue; - const same = isSameSymbol(reference, location); + let same = isSameSymbol(reference, location); let previousLocation = location; - if (same && reference.kind === SyntaxKind.PropertyAccessExpression) { - let node = (location).parent; + if (isLeftHandSideOfAssignment(location)) { + let node = reference; while (node.kind === SyntaxKind.PropertyAccessExpression) { - if (isLeftHandSideOfAssignment(node)) { + 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; } - node = node.parent; } } if (same || isBranchStart(location)) {