Merge branch 'outerControlFlows' of https://github.com/Microsoft/TypeScript into typedefForJsdoc

# Conflicts:
#	src/compiler/binder.ts
This commit is contained in:
zhengbli
2016-05-31 14:02:45 -07:00
16 changed files with 995 additions and 244 deletions
+171 -176
View File
@@ -77,12 +77,14 @@ namespace ts {
// Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
IsBlockScopedContainer = 1 << 1,
HasLocals = 1 << 2,
// The current node is the container of a control flow path. The current control flow should
// be saved and restored, and a new control flow initialized within the container.
IsControlFlowContainer = 1 << 2,
// If the current node is a container that also container that also contains locals. Examples:
//
// Functions, Methods, Modules, Source-files.
IsContainerWithLocals = IsContainer | HasLocals
IsFunctionLike = 1 << 3,
IsFunctionExpression = 1 << 4,
HasLocals = 1 << 5,
IsInterface = 1 << 6,
}
const binder = createBinder();
@@ -103,22 +105,19 @@ namespace ts {
let lastContainer: Node;
let seenThisKeyword: boolean;
// state used by reachability checks
let hasExplicitReturn: boolean;
// state used by control flow analysis
let currentFlow: FlowNode;
let currentBreakTarget: FlowLabel;
let currentContinueTarget: FlowLabel;
let currentReturnTarget: FlowLabel;
let currentTrueTarget: FlowLabel;
let currentFalseTarget: FlowLabel;
let preSwitchCaseFlow: FlowNode;
let activeLabels: ActiveLabel[];
let hasExplicitReturn: boolean;
// state used for emit helpers
let hasClassExtends: boolean;
let hasAsyncFunctions: boolean;
let hasDecorators: boolean;
let hasParameterDecorators: boolean;
let hasJsxSpreadAttribute: boolean;
let emitFlags: NodeFlags;
// 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
@@ -156,18 +155,15 @@ namespace ts {
blockScopeContainer = undefined;
lastContainer = undefined;
seenThisKeyword = false;
hasExplicitReturn = false;
currentFlow = undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
currentReturnTarget = undefined;
currentTrueTarget = undefined;
currentFalseTarget = undefined;
activeLabels = undefined;
hasClassExtends = false;
hasAsyncFunctions = false;
hasDecorators = false;
hasParameterDecorators = false;
hasJsxSpreadAttribute = false;
hasExplicitReturn = false;
emitFlags = NodeFlags.None;
}
return bindSourceFile;
@@ -412,26 +408,13 @@ namespace ts {
// All container nodes are kept on a linked list in declaration order. This list is used by
// the getLocalNameOfContainer function in the type checker to validate that the local name
// used for a container is unique.
function bindChildren(node: Node) {
function bindContainer(node: Node, containerFlags: ContainerFlags) {
// 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;
const saveContainer = container;
const savedBlockScopeContainer = blockScopeContainer;
// This node will now be set as the parent of all of its children as we recurse into them.
parent = node;
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
}
// Depending on what kind of node this is, we may have to adjust the current container
// and block-container. If the current node is a container, then it is automatically
// considered the current block-container as well. Also, for containers that we know
@@ -449,111 +432,90 @@ namespace ts {
// reusing a node from a previous compilation, that node may have had 'locals' created
// 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) {
container = blockScopeContainer = node;
if (containerFlags & ContainerFlags.HasLocals) {
container.locals = {};
}
addToContainerChain(container);
}
else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
blockScopeContainer = node;
blockScopeContainer.locals = undefined;
}
let savedHasExplicitReturn: boolean;
let savedCurrentFlow: FlowNode;
let savedBreakTarget: FlowLabel;
let savedContinueTarget: FlowLabel;
let savedActiveLabels: ActiveLabel[];
const kind = node.kind;
let flags = node.flags;
// reset all reachability check related flags on node (for incremental scenarios)
flags &= ~NodeFlags.ReachabilityCheckFlags;
// reset all emit helper flags on node (for incremental scenarios)
flags &= ~NodeFlags.EmitHelperFlags;
if (kind === SyntaxKind.InterfaceDeclaration) {
seenThisKeyword = false;
}
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
if (saveState) {
savedHasExplicitReturn = hasExplicitReturn;
savedCurrentFlow = currentFlow;
savedBreakTarget = currentBreakTarget;
savedContinueTarget = currentContinueTarget;
savedActiveLabels = activeLabels;
hasExplicitReturn = false;
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
const saveCurrentFlow = currentFlow;
const saveBreakTarget = currentBreakTarget;
const saveContinueTarget = currentContinueTarget;
const saveReturnTarget = currentReturnTarget;
const saveActiveLabels = activeLabels;
const saveHasExplicitReturn = hasExplicitReturn;
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node);
// An IIFE is considered part of the containing control flow. Return statements behave
// similarly to break statements that exit to a label just past the statement body.
if (isIIFE) {
currentReturnTarget = createBranchLabel();
}
else {
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsFunctionExpression) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
}
currentReturnTarget = undefined;
}
currentBreakTarget = undefined;
currentContinueTarget = undefined;
activeLabels = undefined;
hasExplicitReturn = false;
bindChildren(node);
// Reset all reachability check related flags on node (for incremental scenarios)
// Reset all emit helper flags on node (for incremental scenarios)
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
node.flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
}
if (node.kind === SyntaxKind.SourceFile) {
node.flags |= emitFlags;
}
if (isIIFE) {
addAntecedent(currentReturnTarget, currentFlow);
currentFlow = finishFlowLabel(currentReturnTarget);
}
else {
currentFlow = saveCurrentFlow;
}
currentBreakTarget = saveBreakTarget;
currentContinueTarget = saveContinueTarget;
currentReturnTarget = saveReturnTarget;
activeLabels = saveActiveLabels;
hasExplicitReturn = saveHasExplicitReturn;
}
bindReachableStatement(node);
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) {
flags |= NodeFlags.HasExplicitReturn;
}
else if (containerFlags & ContainerFlags.IsInterface) {
seenThisKeyword = false;
bindChildren(node);
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
}
if (kind === SyntaxKind.InterfaceDeclaration) {
flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis;
else {
bindChildren(node);
}
if (kind === SyntaxKind.SourceFile) {
if (hasClassExtends) {
flags |= NodeFlags.HasClassExtends;
}
if (hasDecorators) {
flags |= NodeFlags.HasDecorators;
}
if (hasParameterDecorators) {
flags |= NodeFlags.HasParamDecorators;
}
if (hasAsyncFunctions) {
flags |= NodeFlags.HasAsyncFunctions;
}
if (hasJsxSpreadAttribute) {
flags |= NodeFlags.HasJsxSpreadAttribute;
}
}
node.flags = flags;
if (saveState) {
hasExplicitReturn = savedHasExplicitReturn;
currentFlow = savedCurrentFlow;
currentBreakTarget = savedBreakTarget;
currentContinueTarget = savedContinueTarget;
activeLabels = savedActiveLabels;
}
container = saveContainer;
parent = saveParent;
blockScopeContainer = savedBlockScopeContainer;
}
/**
* Returns true if node and its subnodes were successfully traversed.
* Returning false means that node was not examined and caller needs to dive into the node himself.
*/
function bindReachableStatement(node: Node): void {
function bindChildren(node: Node): void {
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
}
if (checkUnreachable(node)) {
forEachChild(node, bind);
return;
}
switch (node.kind) {
case SyntaxKind.WhileStatement:
bindWhileStatement(<WhileStatement>node);
@@ -606,6 +568,9 @@ namespace ts {
case SyntaxKind.VariableDeclaration:
bindVariableDeclarationFlow(<VariableDeclaration>node);
break;
case SyntaxKind.CallExpression:
bindCallExpressionFlow(<CallExpression>node);
break;
default:
forEachChild(node, bind);
break;
@@ -865,6 +830,9 @@ namespace ts {
bind(node.expression);
if (node.kind === SyntaxKind.ReturnStatement) {
hasExplicitReturn = true;
if (currentReturnTarget) {
addAntecedent(currentReturnTarget, currentFlow);
}
}
currentFlow = unreachableFlow;
}
@@ -1115,11 +1083,28 @@ namespace ts {
}
}
function bindCallExpressionFlow(node: CallExpression) {
// If the target of the call expression is a function expression or arrow function we have
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
// the current control flow (which includes evaluation of the IIFE arguments).
let expr: Expression = node.expression;
while (expr.kind === SyntaxKind.ParenthesizedExpression) {
expr = (<ParenthesizedExpression>expr).expression;
}
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
forEach(node.typeArguments, bind);
forEach(node.arguments, bind);
bind(node.expression);
}
else {
forEachChild(node, bind);
}
}
function getContainerFlags(node: Node): ContainerFlags {
switch (node.kind) {
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.TypeLiteral:
@@ -1127,24 +1112,36 @@ namespace ts {
case SyntaxKind.JSDocRecordType:
return ContainerFlags.IsContainer;
case SyntaxKind.InterfaceDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
case SyntaxKind.SourceFile:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ConstructorType:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.SourceFile:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainerWithLocals;
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
case SyntaxKind.ModuleBlock:
return ContainerFlags.IsControlFlowContainer;
case SyntaxKind.CatchClause:
case SyntaxKind.ForStatement:
@@ -1579,15 +1576,9 @@ namespace ts {
if (!node) {
return;
}
node.parent = parent;
const savedInStrictMode = inStrictMode;
if (!savedInStrictMode) {
updateStrictMode(node);
}
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
const saveInStrictMode = inStrictMode;
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
// and then potentially add the symbol to an appropriate symbol table. Possible
// destination symbol tables are:
//
@@ -1595,47 +1586,40 @@ namespace ts {
// 2) The 'members' table of the current container's symbol.
// 3) The 'locals' table of the current container.
//
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// (like TypeLiterals for example) will not be put in any table.
bindWorker(node);
// 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
// a local should go into for example.
bindChildren(node);
inStrictMode = savedInStrictMode;
}
function updateStrictMode(node: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
updateStrictModeStatementList((<SourceFile | ModuleBlock>node).statements);
return;
case SyntaxKind.Block:
if (isFunctionLike(node.parent)) {
updateStrictModeStatementList((<Block>node).statements);
}
return;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return;
// 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
// a local should go into for example. Since terminal nodes are known not to have
// children, as an optimization we don't process those.
if (node.kind > SyntaxKind.LastToken) {
const saveParent = parent;
parent = node;
const containerFlags = getContainerFlags(node);
if (containerFlags === ContainerFlags.None) {
bindChildren(node);
}
else {
bindContainer(node, containerFlags);
}
parent = saveParent;
}
inStrictMode = saveInStrictMode;
}
function updateStrictModeStatementList(statements: NodeArray<Statement>) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (!inStrictMode) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
}
}
}
}
@@ -1724,7 +1708,7 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
case SyntaxKind.JsxSpreadAttribute:
hasJsxSpreadAttribute = true;
emitFlags |= NodeFlags.HasJsxSpreadAttribute;
return;
case SyntaxKind.CallSignature:
@@ -1770,6 +1754,8 @@ namespace ts {
// Members of classes, interfaces, and modules
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return bindClassLikeDeclaration(<ClassLikeDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
@@ -1796,7 +1782,15 @@ namespace ts {
case SyntaxKind.ExportAssignment:
return bindExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
updateStrictModeStatementList((<SourceFile>node).statements);
return bindSourceFileIfExternalModule();
case SyntaxKind.Block:
if (!isFunctionLike(node.parent)) {
return;
}
// Fall through
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
}
}
@@ -1958,10 +1952,10 @@ namespace ts {
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
hasClassExtends = true;
emitFlags |= NodeFlags.HasClassExtends;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
@@ -2037,8 +2031,7 @@ namespace ts {
if (!isDeclarationFile(file) &&
!isInAmbientContext(node) &&
nodeIsDecorated(node)) {
hasDecorators = true;
hasParameterDecorators = true;
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
}
if (inStrictMode) {
@@ -2065,7 +2058,7 @@ namespace ts {
function bindFunctionDeclaration(node: FunctionDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
@@ -2082,10 +2075,12 @@ namespace ts {
function bindFunctionExpression(node: FunctionExpression) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
if (currentFlow) {
node.flowNode = currentFlow;
}
checkStrictModeFunctionName(<FunctionExpression>node);
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
@@ -2094,10 +2089,10 @@ namespace ts {
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
+45 -39
View File
@@ -7660,7 +7660,7 @@ namespace ts {
getInitialTypeOfBindingElement(<BindingElement>node);
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) {
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
@@ -7706,15 +7706,21 @@ namespace ts {
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
getTypeAtFlowLoopLabel(<FlowLabel>flow);
}
else if (flow.flags & FlowFlags.Unreachable) {
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (<FlowStart>flow).container;
if (container && includeOuterFunctions) {
flow = container.flowNode;
continue;
}
// At the top of the flow we have the initial type.
type = initialType;
}
else {
// Unreachable code errors are reported in the binding phase. Here we
// simply return the declared type to reduce follow-on errors.
type = declaredType;
}
else {
// At the top of the flow we have the initial type.
type = initialType;
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
visitedFlowNodes[visitedFlowCount] = flow;
@@ -8083,6 +8089,17 @@ namespace ts {
return expression;
}
function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
const declarationContainer = getContainingFunctionOrModule(declaration);
let container = getContainingFunctionOrModule(reference);
while (container !== declarationContainer &&
(container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
(includeOuterFunctions || getImmediatelyInvokedFunctionExpression(<FunctionExpression>container))) {
container = getContainingFunctionOrModule(container);
}
return container === declarationContainer;
}
function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
@@ -8139,10 +8156,11 @@ namespace ts {
return type;
}
const declaration = localOrExportSymbol.valueDeclaration;
const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized);
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
@@ -8391,7 +8409,7 @@ namespace ts {
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true);
}
if (isInJavaScriptFile(node)) {
@@ -8637,23 +8655,25 @@ namespace ts {
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
const func = parameter.parent;
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife) {
const indexOfParameter = indexOf(func.parameters, parameter);
if (iife.arguments && indexOfParameter < iife.arguments.length) {
if (parameter.dotDotDotToken) {
const restTypes: Type[] = [];
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
restTypes.push(getTypeOfExpression(iife.arguments[i]));
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife) {
const indexOfParameter = indexOf(func.parameters, parameter);
if (iife.arguments && indexOfParameter < iife.arguments.length) {
if (parameter.dotDotDotToken) {
const restTypes: Type[] = [];
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
restTypes.push(getTypeOfExpression(iife.arguments[i]));
}
return createArrayType(getUnionType(restTypes));
}
return createArrayType(getUnionType(restTypes));
const links = getNodeLinks(iife);
const cached = links.resolvedSignature;
links.resolvedSignature = anySignature;
const type = checkExpression(iife.arguments[indexOfParameter]);
links.resolvedSignature = cached;
return type;
}
const links = getNodeLinks(iife);
const cached = links.resolvedSignature;
links.resolvedSignature = anySignature;
const type = checkExpression(iife.arguments[indexOfParameter]);
links.resolvedSignature = cached;
return type;
}
}
const contextualSignature = getContextualSignature(func);
@@ -8676,20 +8696,6 @@ namespace ts {
return undefined;
}
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
if (isFunctionExpressionOrArrowFunction(func)) {
let prev: Node = func;
let parent: Node = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,
@@ -10003,7 +10009,7 @@ namespace ts {
return propType;
}
}
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
+8
View File
@@ -423,6 +423,7 @@ namespace ts {
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
@@ -1564,6 +1565,13 @@ namespace ts {
id?: number; // Node id used by flow type cache in checker
}
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the container property references the function (which in turn has a flowNode
// property for the containing control flow).
export interface FlowStart extends FlowNode {
container?: FunctionExpression | ArrowFunction;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
export interface FlowLabel extends FlowNode {
antecedents: FlowNode[];
+14
View File
@@ -1007,6 +1007,20 @@ namespace ts {
}
}
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
let prev = func;
let parent = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
/**
* Determines whether a node is a property or element access expression for super.
*/
@@ -0,0 +1,73 @@
//// [constLocalsInFunctionExpressions.ts]
declare function getStringOrNumber(): string | number;
function f1() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => x.length;
}
}
function f2() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = () => x.length;
}
function f3() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = function() { return x.length; };
}
}
function f4() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = function() { return x.length; };
}
function f5() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => () => x.length;
}
}
//// [constLocalsInFunctionExpressions.js]
function f1() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return x.length; };
}
}
function f2() {
var x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
var f = function () { return x.length; };
}
function f3() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return x.length; };
}
}
function f4() {
var x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
var f = function () { return x.length; };
}
function f5() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return function () { return x.length; }; };
}
}
@@ -0,0 +1,95 @@
=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
function f1() {
>f1 : Symbol(f1, Decl(constLocalsInFunctionExpressions.ts, 0, 54))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
const f = () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 5, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
function f2() {
>f2 : Symbol(f2, Decl(constLocalsInFunctionExpressions.ts, 7, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x !== "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
return;
}
const f = () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 14, 9))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
function f3() {
>f3 : Symbol(f3, Decl(constLocalsInFunctionExpressions.ts, 15, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
const f = function() { return x.length; };
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 20, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
function f4() {
>f4 : Symbol(f4, Decl(constLocalsInFunctionExpressions.ts, 22, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x !== "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
return;
}
const f = function() { return x.length; };
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 29, 9))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
function f5() {
>f5 : Symbol(f5, Decl(constLocalsInFunctionExpressions.ts, 30, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
const f = () => () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 35, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
@@ -0,0 +1,121 @@
=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : () => string | number
function f1() {
>f1 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = () => x.length;
>f : () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
}
function f2() {
>f2 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x !== "string") {
>typeof x !== "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
return;
}
const f = () => x.length;
>f : () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
function f3() {
>f3 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = function() { return x.length; };
>f : () => number
>function() { return x.length; } : () => number
>x.length : number
>x : string
>length : number
}
}
function f4() {
>f4 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x !== "string") {
>typeof x !== "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
return;
}
const f = function() { return x.length; };
>f : () => number
>function() { return x.length; } : () => number
>x.length : number
>x : string
>length : number
}
function f5() {
>f5 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = () => () => x.length;
>f : () => () => number
>() => () => x.length : () => () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
}
@@ -0,0 +1,89 @@
//// [controlFlowIIFE.ts]
declare function getStringOrNumber(): string | number;
function f1() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = function() {
return x.length;
}();
}
}
function f2() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = (function() {
return x.length;
})();
}
}
function f3() {
let x = getStringOrNumber();
let y: number;
if (typeof x === "string") {
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
let maybeNumber: number | undefined;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
let test: string | undefined;
if (!test) {
throw new Error('Test is not defined');
}
(() => {
test.slice(1); // No error
})();
//// [controlFlowIIFE.js]
function f1() {
var x = getStringOrNumber();
if (typeof x === "string") {
var n = function () {
return x.length;
}();
}
}
function f2() {
var x = getStringOrNumber();
if (typeof x === "string") {
var n = (function () {
return x.length;
})();
}
}
function f3() {
var x = getStringOrNumber();
var y;
if (typeof x === "string") {
var n = (function (z) { return x.length + y + z; })(y = 1);
}
}
// Repros from #8381
var maybeNumber;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
var test;
if (!test) {
throw new Error('Test is not defined');
}
(function () {
test.slice(1); // No error
})();
@@ -0,0 +1,111 @@
=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
function f1() {
>f1 : Symbol(f1, Decl(controlFlowIIFE.ts, 1, 54))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
let n = function() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 6, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}();
}
}
function f2() {
>f2 : Symbol(f2, Decl(controlFlowIIFE.ts, 10, 1))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
let n = (function() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 15, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
})();
}
}
function f3() {
>f3 : Symbol(f3, Decl(controlFlowIIFE.ts, 19, 1))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
let y: number;
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
let n = (z => x.length + y + z)(y = 1);
>n : Symbol(n, Decl(controlFlowIIFE.ts, 25, 11))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
}
}
// Repros from #8381
let maybeNumber: number | undefined;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
(function () {
maybeNumber = 1;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
})();
maybeNumber++;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
if (maybeNumber !== undefined) {
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
>undefined : Symbol(undefined)
maybeNumber++;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
}
let test: string | undefined;
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
if (!test) {
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
throw new Error('Test is not defined');
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
}
(() => {
test.slice(1); // No error
>test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
})();
@@ -0,0 +1,153 @@
=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : () => string | number
function f1() {
>f1 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = function() {
>n : number
>function() { return x.length; }() : number
>function() { return x.length; } : () => number
return x.length;
>x.length : number
>x : string
>length : number
}();
}
}
function f2() {
>f2 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = (function() {
>n : number
>(function() { return x.length; })() : number
>(function() { return x.length; }) : () => number
>function() { return x.length; } : () => number
return x.length;
>x.length : number
>x : string
>length : number
})();
}
}
function f3() {
>f3 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
let y: number;
>y : number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = (z => x.length + y + z)(y = 1);
>n : number
>(z => x.length + y + z)(y = 1) : number
>(z => x.length + y + z) : (z: number) => number
>z => x.length + y + z : (z: number) => number
>z : number
>x.length + y + z : number
>x.length + y : number
>x.length : number
>x : string
>length : number
>y : number
>z : number
>y = 1 : number
>y : number
>1 : number
}
}
// Repros from #8381
let maybeNumber: number | undefined;
>maybeNumber : number | undefined
(function () {
>(function () { maybeNumber = 1;})() : void
>(function () { maybeNumber = 1;}) : () => void
>function () { maybeNumber = 1;} : () => void
maybeNumber = 1;
>maybeNumber = 1 : number
>maybeNumber : number | undefined
>1 : number
})();
maybeNumber++;
>maybeNumber++ : number
>maybeNumber : number
if (maybeNumber !== undefined) {
>maybeNumber !== undefined : boolean
>maybeNumber : number
>undefined : undefined
maybeNumber++;
>maybeNumber++ : number
>maybeNumber : number
}
let test: string | undefined;
>test : string | undefined
if (!test) {
>!test : boolean
>test : string | undefined
throw new Error('Test is not defined');
>new Error('Test is not defined') : Error
>Error : ErrorConstructor
>'Test is not defined' : string
}
(() => {
>(() => { test.slice(1); // No error})() : void
>(() => { test.slice(1); // No error}) : () => void
>() => { test.slice(1); // No error} : () => void
test.slice(1); // No error
>test.slice(1) : string
>test.slice : (start?: number | undefined, end?: number | undefined) => string
>test : string
>slice : (start?: number | undefined, end?: number | undefined) => string
>1 : number
})();
@@ -41,7 +41,7 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
if (typeof x === "string") {
var y = x; // string;
@@ -121,7 +121,7 @@ function foo4(x) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x) {
if (typeof x === "string") {
var y = x; // string;
@@ -27,9 +27,9 @@ function foo(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 2, 13))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
} ();
}
@@ -60,9 +60,9 @@ function foo2(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
} (x); // x here is narrowed to number | boolean
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14))
@@ -91,9 +91,9 @@ function foo3(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 22, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
})();
}
@@ -123,14 +123,14 @@ function foo4(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
})(x); // x here is narrowed to number | boolean
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14))
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
>foo5 : Symbol(foo5, Decl(typeGuardsInFunctionAndModuleBlock.ts, 41, 1))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 43, 14))
@@ -21,14 +21,14 @@ 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
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@@ -40,7 +40,7 @@ function foo(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
} ();
@@ -66,14 +66,14 @@ 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
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@@ -85,7 +85,7 @@ function foo2(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
} (x); // x here is narrowed to number | boolean
@@ -111,14 +111,14 @@ 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
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@@ -130,7 +130,7 @@ function foo3(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
})();
@@ -156,14 +156,14 @@ 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
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@@ -175,13 +175,13 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
})(x); // x here is narrowed to number | boolean
>x : number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
>foo5 : (x: number | string | boolean) => void
>x : number | string | boolean
@@ -0,0 +1,38 @@
declare function getStringOrNumber(): string | number;
function f1() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => x.length;
}
}
function f2() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = () => x.length;
}
function f3() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = function() { return x.length; };
}
}
function f4() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = function() { return x.length; };
}
function f5() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => () => x.length;
}
}
@@ -0,0 +1,48 @@
// @strictNullChecks: true
declare function getStringOrNumber(): string | number;
function f1() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = function() {
return x.length;
}();
}
}
function f2() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = (function() {
return x.length;
})();
}
}
function f3() {
let x = getStringOrNumber();
let y: number;
if (typeof x === "string") {
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
let maybeNumber: number | undefined;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
let test: string | undefined;
if (!test) {
throw new Error('Test is not defined');
}
(() => {
test.slice(1); // No error
})();
@@ -40,7 +40,7 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
if (typeof x === "string") {
var y = x; // string;