Narrowed consts flow through object literal or class expression method

Fixes #10501
This commit is contained in:
Sheetal Nandi
2016-10-07 17:50:05 -07:00
parent d543028226
commit 402f1f6bf8
7 changed files with 117 additions and 32 deletions
+11 -3
View File
@@ -84,6 +84,7 @@ namespace ts {
IsFunctionExpression = 1 << 4,
HasLocals = 1 << 5,
IsInterface = 1 << 6,
IsObjectLiteralOrClassExpressionMethod = 1 << 7,
}
const binder = createBinder();
@@ -486,8 +487,8 @@ namespace ts {
}
else {
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsFunctionExpression) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
}
currentReturnTarget = undefined;
}
@@ -1237,9 +1238,12 @@ namespace ts {
case SyntaxKind.SourceFile:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
case SyntaxKind.MethodDeclaration:
if (isObjectLiteralOrClassExpressionMethod(node)) {
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethod;
}
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
@@ -2238,6 +2242,10 @@ namespace ts {
}
}
if (currentFlow && isObjectLiteralOrClassExpressionMethod(node)) {
node.flowNode = currentFlow;
}
return hasDynamicName(node)
? bindAnonymousDeclaration(node, symbolFlags, "__computed")
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+3 -1
View File
@@ -9163,7 +9163,9 @@ namespace ts {
// a const variable or parameter from an outer function, we extend the origin of the control flow
// analysis to include the immediately enclosing function.
while (flowContainer !== declarationContainer &&
(flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) &&
(flowContainer.kind === SyntaxKind.FunctionExpression ||
flowContainer.kind === SyntaxKind.ArrowFunction ||
isObjectLiteralOrClassExpressionMethod(flowContainer)) &&
(isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
flowContainer = getControlFlowContainer(flowContainer);
}
+1 -1
View File
@@ -1922,7 +1922,7 @@ namespace ts {
// 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;
container?: FunctionExpression | ArrowFunction | MethodDeclaration;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
+6
View File
@@ -895,6 +895,12 @@ namespace ts {
return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression;
}
export function isObjectLiteralOrClassExpressionMethod(node: Node): node is MethodDeclaration {
return node.kind === SyntaxKind.MethodDeclaration &&
(node.parent.kind === SyntaxKind.ObjectLiteralExpression ||
node.parent.kind === SyntaxKind.ClassExpression);
}
export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate {
return predicate && predicate.kind === TypePredicateKind.Identifier;
}
@@ -1,27 +0,0 @@
tests/cases/compiler/narrowedConstInMethod.ts(6,28): error TS2531: Object is possibly 'null'.
tests/cases/compiler/narrowedConstInMethod.ts(15,28): error TS2531: Object is possibly 'null'.
==== tests/cases/compiler/narrowedConstInMethod.ts (2 errors) ====
function f() {
const x: string | null = <any>{};
if (x !== null) {
return {
bar() { return x.length; } // Error: possibly null x
~
!!! error TS2531: Object is possibly 'null'.
};
}
}
function f2() {
const x: string | null = <any>{};
if (x !== null) {
return class {
bar() { return x.length; } // Error: possibly null x
~
!!! error TS2531: Object is possibly 'null'.
};
}
}
@@ -0,0 +1,41 @@
=== tests/cases/compiler/narrowedConstInMethod.ts ===
function f() {
>f : Symbol(f, Decl(narrowedConstInMethod.ts, 0, 0))
const x: string | null = <any>{};
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 2, 9))
if (x !== null) {
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 2, 9))
return {
bar() { return x.length; } // Error: possibly null x
>bar : Symbol(bar, Decl(narrowedConstInMethod.ts, 4, 16))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 2, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
};
}
}
function f2() {
>f2 : Symbol(f2, Decl(narrowedConstInMethod.ts, 8, 1))
const x: string | null = <any>{};
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 11, 9))
if (x !== null) {
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 11, 9))
return class {
bar() { return x.length; } // Error: possibly null x
>bar : Symbol((Anonymous class).bar, Decl(narrowedConstInMethod.ts, 13, 22))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(narrowedConstInMethod.ts, 11, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
};
}
}
@@ -0,0 +1,55 @@
=== tests/cases/compiler/narrowedConstInMethod.ts ===
function f() {
>f : () => { bar(): number; } | undefined
const x: string | null = <any>{};
>x : string | null
>null : null
><any>{} : any
>{} : {}
if (x !== null) {
>x !== null : boolean
>x : string | null
>null : null
return {
>{ bar() { return x.length; } // Error: possibly null x } : { bar(): number; }
bar() { return x.length; } // Error: possibly null x
>bar : () => number
>x.length : number
>x : string
>length : number
};
}
}
function f2() {
>f2 : () => typeof (Anonymous class) | undefined
const x: string | null = <any>{};
>x : string | null
>null : null
><any>{} : any
>{} : {}
if (x !== null) {
>x !== null : boolean
>x : string | null
>null : null
return class {
>class { bar() { return x.length; } // Error: possibly null x } : typeof (Anonymous class)
bar() { return x.length; } // Error: possibly null x
>bar : () => number
>x.length : number
>x : string
>length : number
};
}
}