-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f698783
commit bdbc388
Showing
4 changed files
with
868 additions
and
0 deletions.
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
tests/baselines/reference/narrowingPastLastAssignment.errors.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
narrowingPastLastAssignment.ts(67,9): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. | ||
narrowingPastLastAssignment.ts(69,20): error TS7005: Variable 'x' implicitly has an 'any' type. | ||
|
||
|
||
==== narrowingPastLastAssignment.ts (2 errors) ==== | ||
function action(f: Function) {} | ||
|
||
// Narrowings are preserved in closures created past last assignment | ||
|
||
function f1(x: string | number) { | ||
x = "abc"; | ||
action(() => { x /* string | number */ }); | ||
x = 42; | ||
action(() => { x /* number */ }); | ||
} | ||
|
||
// Narrowings are not preserved in inner function and class declarations (due to hoisting) | ||
|
||
function f2() { | ||
let x: string | number; | ||
x = 42; | ||
let a = () => { x /* number */ }; | ||
let f = function() { x /* number */ }; | ||
let C = class { | ||
foo() { x /* number */ } | ||
}; | ||
let o = { | ||
foo() { x /* number */ } | ||
}; | ||
function g() { x /* string | number */ } | ||
class A { | ||
foo() { x /* string | number */ } | ||
} | ||
} | ||
|
||
// Narrowings are not preserved when assignments occur in inner functions | ||
|
||
function f3(x: string | number) { | ||
action(() => { x = "abc" }); | ||
x = 42; | ||
action(() => { x /* string | number */ }); | ||
} | ||
|
||
// Assignment effects in compoud statements extend to the entire statement | ||
|
||
function f4(cond: () => boolean) { | ||
let x: string | number = 0; | ||
while (cond()) { | ||
x = "abc"; | ||
action(() => { x /* string | number */ }); | ||
x = 42; | ||
action(() => { x /* string | number */ }); | ||
} | ||
action(() => { x /* number */ }); | ||
} | ||
|
||
function f5(x: string | number, cond: () => boolean) { | ||
if (cond()) { | ||
x = 1; | ||
action(() => { x /* string | number */ }); | ||
} | ||
else { | ||
x = 2; | ||
action(() => { x /* string | number */ }); | ||
} | ||
action(() => { x /* number */ }); | ||
} | ||
|
||
// Implicit any variables have a known type following last assignment | ||
|
||
function f6() { | ||
let x; | ||
~ | ||
!!! error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. | ||
x = "abc"; | ||
action(() => { x }); // Error | ||
~ | ||
!!! error TS7005: Variable 'x' implicitly has an 'any' type. | ||
x = 42; | ||
action(() => { x /* number */ }); | ||
} | ||
|
||
// Narrowings on catch variables are preserved past last assignment | ||
|
||
function f7() { | ||
try { | ||
} | ||
catch (e) { | ||
if (e instanceof Error) { | ||
let f = () => { e /* Error */ } | ||
} | ||
} | ||
} | ||
|
||
// Repros from #35124 | ||
|
||
function f10() { | ||
let i: number | undefined; | ||
i = 0; | ||
return (k: number) => k === i + 1; | ||
} | ||
|
||
function makeAdder(n?: number) { | ||
n ??= 0; | ||
return (m: number) => n + m; | ||
} | ||
|
||
function f11() { | ||
let r; | ||
r = "b"; | ||
() => r; | ||
} | ||
|
||
// Repro from #52104 | ||
|
||
const fooMap: Map<string,Array<number>> = new Map() | ||
const values = [1, 2, 3, 4, 5]; | ||
let foo = fooMap.get("a"); | ||
if (foo == null) { | ||
foo = []; | ||
} | ||
values.forEach(v => foo.push(v)); | ||
|
274 changes: 274 additions & 0 deletions
274
tests/baselines/reference/narrowingPastLastAssignment.symbols
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
//// [tests/cases/compiler/narrowingPastLastAssignment.ts] //// | ||
|
||
=== narrowingPastLastAssignment.ts === | ||
function action(f: Function) {} | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>f : Symbol(f, Decl(narrowingPastLastAssignment.ts, 0, 16)) | ||
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.decorators.d.ts, --, --)) | ||
|
||
// Narrowings are preserved in closures created past last assignment | ||
|
||
function f1(x: string | number) { | ||
>f1 : Symbol(f1, Decl(narrowingPastLastAssignment.ts, 0, 31)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 4, 12)) | ||
|
||
x = "abc"; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 4, 12)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 4, 12)) | ||
|
||
x = 42; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 4, 12)) | ||
|
||
action(() => { x /* number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 4, 12)) | ||
} | ||
|
||
// Narrowings are not preserved in inner function and class declarations (due to hoisting) | ||
|
||
function f2() { | ||
>f2 : Symbol(f2, Decl(narrowingPastLastAssignment.ts, 9, 1)) | ||
|
||
let x: string | number; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
x = 42; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
let a = () => { x /* number */ }; | ||
>a : Symbol(a, Decl(narrowingPastLastAssignment.ts, 16, 7)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
let f = function() { x /* number */ }; | ||
>f : Symbol(f, Decl(narrowingPastLastAssignment.ts, 17, 7)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
let C = class { | ||
>C : Symbol(C, Decl(narrowingPastLastAssignment.ts, 18, 7)) | ||
|
||
foo() { x /* number */ } | ||
>foo : Symbol(C.foo, Decl(narrowingPastLastAssignment.ts, 18, 19)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
}; | ||
let o = { | ||
>o : Symbol(o, Decl(narrowingPastLastAssignment.ts, 21, 7)) | ||
|
||
foo() { x /* number */ } | ||
>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 21, 13)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
}; | ||
function g() { x /* string | number */ } | ||
>g : Symbol(g, Decl(narrowingPastLastAssignment.ts, 23, 6)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
|
||
class A { | ||
>A : Symbol(A, Decl(narrowingPastLastAssignment.ts, 24, 44)) | ||
|
||
foo() { x /* string | number */ } | ||
>foo : Symbol(A.foo, Decl(narrowingPastLastAssignment.ts, 25, 13)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 14, 7)) | ||
} | ||
} | ||
|
||
// Narrowings are not preserved when assignments occur in inner functions | ||
|
||
function f3(x: string | number) { | ||
>f3 : Symbol(f3, Decl(narrowingPastLastAssignment.ts, 28, 1)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 32, 12)) | ||
|
||
action(() => { x = "abc" }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 32, 12)) | ||
|
||
x = 42; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 32, 12)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 32, 12)) | ||
} | ||
|
||
// Assignment effects in compoud statements extend to the entire statement | ||
|
||
function f4(cond: () => boolean) { | ||
>f4 : Symbol(f4, Decl(narrowingPastLastAssignment.ts, 36, 1)) | ||
>cond : Symbol(cond, Decl(narrowingPastLastAssignment.ts, 40, 12)) | ||
|
||
let x: string | number = 0; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
|
||
while (cond()) { | ||
>cond : Symbol(cond, Decl(narrowingPastLastAssignment.ts, 40, 12)) | ||
|
||
x = "abc"; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
|
||
x = 42; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
} | ||
action(() => { x /* number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 41, 7)) | ||
} | ||
|
||
function f5(x: string | number, cond: () => boolean) { | ||
>f5 : Symbol(f5, Decl(narrowingPastLastAssignment.ts, 49, 1)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
>cond : Symbol(cond, Decl(narrowingPastLastAssignment.ts, 51, 31)) | ||
|
||
if (cond()) { | ||
>cond : Symbol(cond, Decl(narrowingPastLastAssignment.ts, 51, 31)) | ||
|
||
x = 1; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
} | ||
else { | ||
x = 2; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
|
||
action(() => { x /* string | number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
} | ||
action(() => { x /* number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 51, 12)) | ||
} | ||
|
||
// Implicit any variables have a known type following last assignment | ||
|
||
function f6() { | ||
>f6 : Symbol(f6, Decl(narrowingPastLastAssignment.ts, 61, 1)) | ||
|
||
let x; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 66, 7)) | ||
|
||
x = "abc"; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 66, 7)) | ||
|
||
action(() => { x }); // Error | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 66, 7)) | ||
|
||
x = 42; | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 66, 7)) | ||
|
||
action(() => { x /* number */ }); | ||
>action : Symbol(action, Decl(narrowingPastLastAssignment.ts, 0, 0)) | ||
>x : Symbol(x, Decl(narrowingPastLastAssignment.ts, 66, 7)) | ||
} | ||
|
||
// Narrowings on catch variables are preserved past last assignment | ||
|
||
function f7() { | ||
>f7 : Symbol(f7, Decl(narrowingPastLastAssignment.ts, 71, 1)) | ||
|
||
try { | ||
} | ||
catch (e) { | ||
>e : Symbol(e, Decl(narrowingPastLastAssignment.ts, 78, 11)) | ||
|
||
if (e instanceof Error) { | ||
>e : Symbol(e, Decl(narrowingPastLastAssignment.ts, 78, 11)) | ||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) | ||
|
||
let f = () => { e /* Error */ } | ||
>f : Symbol(f, Decl(narrowingPastLastAssignment.ts, 80, 15)) | ||
>e : Symbol(e, Decl(narrowingPastLastAssignment.ts, 78, 11)) | ||
} | ||
} | ||
} | ||
|
||
// Repros from #35124 | ||
|
||
function f10() { | ||
>f10 : Symbol(f10, Decl(narrowingPastLastAssignment.ts, 83, 1)) | ||
|
||
let i: number | undefined; | ||
>i : Symbol(i, Decl(narrowingPastLastAssignment.ts, 88, 7)) | ||
|
||
i = 0; | ||
>i : Symbol(i, Decl(narrowingPastLastAssignment.ts, 88, 7)) | ||
|
||
return (k: number) => k === i + 1; | ||
>k : Symbol(k, Decl(narrowingPastLastAssignment.ts, 90, 12)) | ||
>k : Symbol(k, Decl(narrowingPastLastAssignment.ts, 90, 12)) | ||
>i : Symbol(i, Decl(narrowingPastLastAssignment.ts, 88, 7)) | ||
} | ||
|
||
function makeAdder(n?: number) { | ||
>makeAdder : Symbol(makeAdder, Decl(narrowingPastLastAssignment.ts, 91, 1)) | ||
>n : Symbol(n, Decl(narrowingPastLastAssignment.ts, 93, 19)) | ||
|
||
n ??= 0; | ||
>n : Symbol(n, Decl(narrowingPastLastAssignment.ts, 93, 19)) | ||
|
||
return (m: number) => n + m; | ||
>m : Symbol(m, Decl(narrowingPastLastAssignment.ts, 95, 12)) | ||
>n : Symbol(n, Decl(narrowingPastLastAssignment.ts, 93, 19)) | ||
>m : Symbol(m, Decl(narrowingPastLastAssignment.ts, 95, 12)) | ||
} | ||
|
||
function f11() { | ||
>f11 : Symbol(f11, Decl(narrowingPastLastAssignment.ts, 96, 1)) | ||
|
||
let r; | ||
>r : Symbol(r, Decl(narrowingPastLastAssignment.ts, 99, 7)) | ||
|
||
r = "b"; | ||
>r : Symbol(r, Decl(narrowingPastLastAssignment.ts, 99, 7)) | ||
|
||
() => r; | ||
>r : Symbol(r, Decl(narrowingPastLastAssignment.ts, 99, 7)) | ||
} | ||
|
||
// Repro from #52104 | ||
|
||
const fooMap: Map<string,Array<number>> = new Map() | ||
>fooMap : Symbol(fooMap, Decl(narrowingPastLastAssignment.ts, 106, 5)) | ||
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) | ||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) | ||
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) | ||
|
||
const values = [1, 2, 3, 4, 5]; | ||
>values : Symbol(values, Decl(narrowingPastLastAssignment.ts, 107, 5)) | ||
|
||
let foo = fooMap.get("a"); | ||
>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 108, 3)) | ||
>fooMap.get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --)) | ||
>fooMap : Symbol(fooMap, Decl(narrowingPastLastAssignment.ts, 106, 5)) | ||
>get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --)) | ||
|
||
if (foo == null) { | ||
>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 108, 3)) | ||
|
||
foo = []; | ||
>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 108, 3)) | ||
} | ||
values.forEach(v => foo.push(v)); | ||
>values.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) | ||
>values : Symbol(values, Decl(narrowingPastLastAssignment.ts, 107, 5)) | ||
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) | ||
>v : Symbol(v, Decl(narrowingPastLastAssignment.ts, 112, 15)) | ||
>foo.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) | ||
>foo : Symbol(foo, Decl(narrowingPastLastAssignment.ts, 108, 3)) | ||
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) | ||
>v : Symbol(v, Decl(narrowingPastLastAssignment.ts, 112, 15)) | ||
|
Oops, something went wrong.