mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'outerControlFlows' of https://github.com/Microsoft/TypeScript into typedefForJsdoc
# Conflicts: # src/compiler/binder.ts
This commit is contained in:
+171
-176
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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
|
||||
})();
|
||||
+1
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user