diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 009447df19a..256a84bbde7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6427,6 +6427,8 @@ namespace ts { // Only narrow when symbol is variable of type any or an object, union, or type parameter type if (node && symbol.flags & SymbolFlags.Variable) { if (isTypeAny(type) || type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { + const declaration = getDeclarationOfKind(symbol, SyntaxKind.VariableDeclaration); + const top = declaration && getDeclarationContainer(declaration); const originalType = type; const nodeStack: {node: Node, child: Node}[] = []; loop: while (node.parent) { @@ -6440,15 +6442,12 @@ namespace ts { break; case SyntaxKind.SourceFile: case SyntaxKind.ModuleDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.Constructor: - // Stop at the first containing function or module declaration + // Stop at the first containing file or module declaration break loop; } + if (node === top) { + break; + } } let nodes: {node: Node, child: Node}; diff --git a/tests/baselines/reference/typeGuardInClass.js b/tests/baselines/reference/typeGuardInClass.js new file mode 100644 index 00000000000..0ce25a7c11c --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.js @@ -0,0 +1,37 @@ +//// [typeGuardInClass.ts] +let x: string | number; + +if (typeof x === "string") { + let n = class { + constructor() { + let y: string = x; + } + } +} +else { + let m = class { + constructor() { + let y: number = x; + } + } +} + + +//// [typeGuardInClass.js] +var x; +if (typeof x === "string") { + var n = (function () { + function class_1() { + var y = x; + } + return class_1; + })(); +} +else { + var m = (function () { + function class_2() { + var y = x; + } + return class_2; + })(); +} diff --git a/tests/baselines/reference/typeGuardInClass.symbols b/tests/baselines/reference/typeGuardInClass.symbols new file mode 100644 index 00000000000..cc0e745e4de --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.symbols @@ -0,0 +1,29 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts === +let x: string | number; +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + +if (typeof x === "string") { +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + + let n = class { +>n : Symbol(n, Decl(typeGuardInClass.ts, 3, 7)) + + constructor() { + let y: string = x; +>y : Symbol(y, Decl(typeGuardInClass.ts, 5, 15)) +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + } + } +} +else { + let m = class { +>m : Symbol(m, Decl(typeGuardInClass.ts, 10, 7)) + + constructor() { + let y: number = x; +>y : Symbol(y, Decl(typeGuardInClass.ts, 12, 15)) +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + } + } +} + diff --git a/tests/baselines/reference/typeGuardInClass.types b/tests/baselines/reference/typeGuardInClass.types new file mode 100644 index 00000000000..93fe9f28c5e --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.types @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts === +let x: string | number; +>x : string | number + +if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + let n = class { +>n : typeof (Anonymous class) +>class { constructor() { let y: string = x; } } : typeof (Anonymous class) + + constructor() { + let y: string = x; +>y : string +>x : string + } + } +} +else { + let m = class { +>m : typeof (Anonymous class) +>class { constructor() { let y: number = x; } } : typeof (Anonymous class) + + constructor() { + let y: number = x; +>y : number +>x : number + } + } +} + diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js index 7cf19dac0ad..91443a9ae78 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js @@ -41,12 +41,12 @@ function foo4(x: number | string | boolean) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions, but not nested function declarations +// Type guards affect nested function expressions and nested function declarations function foo5(x: number | string | boolean) { if (typeof x === "string") { var y = x; // string; function foo() { - var z = x; // number | string | boolean, type guard has no effect + var z = x; // string } } } @@ -121,12 +121,12 @@ function foo4(x) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions, but not nested function declarations +// Type guards affect nested function expressions and nested function declarations function foo5(x) { if (typeof x === "string") { var y = x; // string; function foo() { - var z = x; // number | string | boolean, type guard has no effect + var z = x; // string } } } diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols index 2f3232b0225..34810f303db 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols @@ -130,7 +130,7 @@ function foo4(x: number | string | boolean) { })(x); // x here is narrowed to number | boolean >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14)) } -// Type guards affect nested function expressions, but not nested function declarations +// Type guards affect nested function expressions and 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)) @@ -145,7 +145,7 @@ function foo5(x: number | string | boolean) { function foo() { >foo : Symbol(foo, Decl(typeGuardsInFunctionAndModuleBlock.ts, 45, 18)) - var z = x; // number | string | boolean, type guard has no effect + var z = x; // string >z : Symbol(z, Decl(typeGuardsInFunctionAndModuleBlock.ts, 47, 15)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 43, 14)) } diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types index 0a084d6eb54..f7d56ed17d1 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types @@ -181,7 +181,7 @@ function foo4(x: number | string | boolean) { })(x); // x here is narrowed to number | boolean >x : number | boolean } -// Type guards affect nested function expressions, but not nested function declarations +// Type guards affect nested function expressions and nested function declarations function foo5(x: number | string | boolean) { >foo5 : (x: number | string | boolean) => void >x : number | string | boolean @@ -199,9 +199,9 @@ function foo5(x: number | string | boolean) { function foo() { >foo : () => void - var z = x; // number | string | boolean, type guard has no effect ->z : number | string | boolean ->x : number | string | boolean + var z = x; // string +>z : string +>x : string } } } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts new file mode 100644 index 00000000000..aa394aaa75b --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts @@ -0,0 +1,16 @@ +let x: string | number; + +if (typeof x === "string") { + let n = class { + constructor() { + let y: string = x; + } + } +} +else { + let m = class { + constructor() { + let y: number = x; + } + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts index ec23b75b590..5dbc925d04b 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts @@ -40,12 +40,12 @@ function foo4(x: number | string | boolean) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions, but not nested function declarations +// Type guards affect nested function expressions and nested function declarations function foo5(x: number | string | boolean) { if (typeof x === "string") { var y = x; // string; function foo() { - var z = x; // number | string | boolean, type guard has no effect + var z = x; // string } } }