Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg committed Dec 31, 2023
1 parent f698783 commit bdbc388
Show file tree
Hide file tree
Showing 4 changed files with 868 additions and 0 deletions.
123 changes: 123 additions & 0 deletions tests/baselines/reference/narrowingPastLastAssignment.errors.txt
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 tests/baselines/reference/narrowingPastLastAssignment.symbols
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))

0 comments on commit bdbc388

Please sign in to comment.