From 6c29ddf16203b0bd7a88081596c061a346455106 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 11 Nov 2015 15:30:09 -0800 Subject: [PATCH 1/3] loosen walk up containers, add extra test --- src/compiler/checker.ts | 8 +--- tests/baselines/reference/typeGuardInClass.js | 37 +++++++++++++++++++ .../reference/typeGuardInClass.symbols | 27 ++++++++++++++ .../reference/typeGuardInClass.types | 32 ++++++++++++++++ .../typeGuardsInFunctionAndModuleBlock.js | 8 ++-- ...typeGuardsInFunctionAndModuleBlock.symbols | 4 +- .../typeGuardsInFunctionAndModuleBlock.types | 8 ++-- .../typeGuards/typeGuardInClass.ts | 16 ++++++++ .../typeGuardsInFunctionAndModuleBlock.ts | 4 +- 9 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/typeGuardInClass.js create mode 100644 tests/baselines/reference/typeGuardInClass.symbols create mode 100644 tests/baselines/reference/typeGuardInClass.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7856e3f4cda..615d6f1834c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6399,13 +6399,7 @@ 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; } } diff --git a/tests/baselines/reference/typeGuardInClass.js b/tests/baselines/reference/typeGuardInClass.js new file mode 100644 index 00000000000..44bda9f4cb5 --- /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() { + x; // Should be "string" + } + } +} +else { + let m = class { + constructor() { + x; // Should be "number" + } + } +} + + +//// [typeGuardInClass.js] +var x; +if (typeof x === "string") { + var n = (function () { + function class_1() { + x; // Should be "string" + } + return class_1; + })(); +} +else { + var m = (function () { + function class_2() { + x; // Should be "number" + } + return class_2; + })(); +} diff --git a/tests/baselines/reference/typeGuardInClass.symbols b/tests/baselines/reference/typeGuardInClass.symbols new file mode 100644 index 00000000000..66de1651859 --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.symbols @@ -0,0 +1,27 @@ +=== 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() { + x; // Should be "string" +>x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) + } + } +} +else { + let m = class { +>m : Symbol(m, Decl(typeGuardInClass.ts, 10, 7)) + + constructor() { + x; // Should be "number" +>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..8552b58508e --- /dev/null +++ b/tests/baselines/reference/typeGuardInClass.types @@ -0,0 +1,32 @@ +=== 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() { x; // Should be "string" } } : typeof (Anonymous class) + + constructor() { + x; // Should be "string" +>x : string + } + } +} +else { + let m = class { +>m : typeof (Anonymous class) +>class { constructor() { x; // Should be "number" } } : typeof (Anonymous class) + + constructor() { + x; // Should be "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..f88088744fc --- /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() { + x; // Should be "string" + } + } +} +else { + let m = class { + constructor() { + x; // Should be "number" + } + } +} 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 } } } From 472afc4e16771cf94d71b77b02a507fefd6c8651 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 11 Nov 2015 15:48:41 -0800 Subject: [PATCH 2/3] add condition to bail at containing level --- src/compiler/checker.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 615d6f1834c..2eaf91b9614 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6386,6 +6386,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) { @@ -6402,6 +6404,9 @@ namespace ts { // Stop at the first containing file or module declaration break loop; } + if (node === top) { + break; + } } let nodes: {node: Node, child: Node}; From 9531d929c703dade551c2d0497574b9e8b2f1dff Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 17 Nov 2015 14:34:02 -0800 Subject: [PATCH 3/3] update test with pr feedback --- tests/baselines/reference/typeGuardInClass.js | 8 ++++---- tests/baselines/reference/typeGuardInClass.symbols | 6 ++++-- tests/baselines/reference/typeGuardInClass.types | 10 ++++++---- .../expressions/typeGuards/typeGuardInClass.ts | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/baselines/reference/typeGuardInClass.js b/tests/baselines/reference/typeGuardInClass.js index 44bda9f4cb5..0ce25a7c11c 100644 --- a/tests/baselines/reference/typeGuardInClass.js +++ b/tests/baselines/reference/typeGuardInClass.js @@ -4,14 +4,14 @@ let x: string | number; if (typeof x === "string") { let n = class { constructor() { - x; // Should be "string" + let y: string = x; } } } else { let m = class { constructor() { - x; // Should be "number" + let y: number = x; } } } @@ -22,7 +22,7 @@ var x; if (typeof x === "string") { var n = (function () { function class_1() { - x; // Should be "string" + var y = x; } return class_1; })(); @@ -30,7 +30,7 @@ if (typeof x === "string") { else { var m = (function () { function class_2() { - x; // Should be "number" + var y = x; } return class_2; })(); diff --git a/tests/baselines/reference/typeGuardInClass.symbols b/tests/baselines/reference/typeGuardInClass.symbols index 66de1651859..cc0e745e4de 100644 --- a/tests/baselines/reference/typeGuardInClass.symbols +++ b/tests/baselines/reference/typeGuardInClass.symbols @@ -9,7 +9,8 @@ if (typeof x === "string") { >n : Symbol(n, Decl(typeGuardInClass.ts, 3, 7)) constructor() { - x; // Should be "string" + let y: string = x; +>y : Symbol(y, Decl(typeGuardInClass.ts, 5, 15)) >x : Symbol(x, Decl(typeGuardInClass.ts, 0, 3)) } } @@ -19,7 +20,8 @@ else { >m : Symbol(m, Decl(typeGuardInClass.ts, 10, 7)) constructor() { - x; // Should be "number" + 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 index 8552b58508e..93fe9f28c5e 100644 --- a/tests/baselines/reference/typeGuardInClass.types +++ b/tests/baselines/reference/typeGuardInClass.types @@ -10,10 +10,11 @@ if (typeof x === "string") { let n = class { >n : typeof (Anonymous class) ->class { constructor() { x; // Should be "string" } } : typeof (Anonymous class) +>class { constructor() { let y: string = x; } } : typeof (Anonymous class) constructor() { - x; // Should be "string" + let y: string = x; +>y : string >x : string } } @@ -21,10 +22,11 @@ if (typeof x === "string") { else { let m = class { >m : typeof (Anonymous class) ->class { constructor() { x; // Should be "number" } } : typeof (Anonymous class) +>class { constructor() { let y: number = x; } } : typeof (Anonymous class) constructor() { - x; // Should be "number" + let y: number = x; +>y : number >x : number } } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts index f88088744fc..aa394aaa75b 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardInClass.ts @@ -3,14 +3,14 @@ let x: string | number; if (typeof x === "string") { let n = class { constructor() { - x; // Should be "string" + let y: string = x; } } } else { let m = class { constructor() { - x; // Should be "number" + let y: number = x; } } }