Skip to content

Commit b18141b

Browse files
authored
Do not reset control flow analysis on index signatures (#48427)
* do not flag index signatures as a control flow container * add tests and baselines
1 parent b975dfa commit b18141b

File tree

6 files changed

+251
-1
lines changed

6 files changed

+251
-1
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,7 @@ namespace ts {
18181818
case SyntaxKind.ModuleDeclaration:
18191819
case SyntaxKind.TypeAliasDeclaration:
18201820
case SyntaxKind.MappedType:
1821+
case SyntaxKind.IndexSignature:
18211822
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
18221823

18231824
case SyntaxKind.SourceFile:
@@ -1838,7 +1839,6 @@ namespace ts {
18381839
case SyntaxKind.JSDocFunctionType:
18391840
case SyntaxKind.FunctionType:
18401841
case SyntaxKind.ConstructSignature:
1841-
case SyntaxKind.IndexSignature:
18421842
case SyntaxKind.ConstructorType:
18431843
case SyntaxKind.ClassStaticBlockDeclaration:
18441844
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/compiler/controlFlowForIndexSignatures.ts(20,23): error TS2322: Type 'number' is not assignable to type 'string'.
2+
3+
4+
==== tests/cases/compiler/controlFlowForIndexSignatures.ts (1 errors) ====
5+
type Foo = { bar: string };
6+
const boo: Foo = { bar: 'bar' };
7+
8+
function a(aboo1?: Foo) {
9+
if (!aboo1) return;
10+
const aboo2: { [key: string]: typeof aboo1.bar } = boo;
11+
}
12+
13+
declare let b: Foo | undefined;
14+
if (b) {
15+
const bboo: { [key: string]: typeof b.bar } = boo;
16+
}
17+
b = boo;
18+
const bboo: { [key: string]: typeof b.bar } = boo;
19+
20+
declare let c: string | number;
21+
if (typeof c === 'string') {
22+
type C = { [key: string]: typeof c };
23+
const boo1: C = { bar: 'works' };
24+
const boo2: C = { bar: 1 }; // should error
25+
~~~
26+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
27+
!!! related TS6501 tests/cases/compiler/controlFlowForIndexSignatures.ts:18:16: The expected type comes from this index signature.
28+
}
29+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [controlFlowForIndexSignatures.ts]
2+
type Foo = { bar: string };
3+
const boo: Foo = { bar: 'bar' };
4+
5+
function a(aboo1?: Foo) {
6+
if (!aboo1) return;
7+
const aboo2: { [key: string]: typeof aboo1.bar } = boo;
8+
}
9+
10+
declare let b: Foo | undefined;
11+
if (b) {
12+
const bboo: { [key: string]: typeof b.bar } = boo;
13+
}
14+
b = boo;
15+
const bboo: { [key: string]: typeof b.bar } = boo;
16+
17+
declare let c: string | number;
18+
if (typeof c === 'string') {
19+
type C = { [key: string]: typeof c };
20+
const boo1: C = { bar: 'works' };
21+
const boo2: C = { bar: 1 }; // should error
22+
}
23+
24+
25+
//// [controlFlowForIndexSignatures.js]
26+
var boo = { bar: 'bar' };
27+
function a(aboo1) {
28+
if (!aboo1)
29+
return;
30+
var aboo2 = boo;
31+
}
32+
if (b) {
33+
var bboo_1 = boo;
34+
}
35+
b = boo;
36+
var bboo = boo;
37+
if (typeof c === 'string') {
38+
var boo1 = { bar: 'works' };
39+
var boo2 = { bar: 1 }; // should error
40+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
=== tests/cases/compiler/controlFlowForIndexSignatures.ts ===
2+
type Foo = { bar: string };
3+
>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0))
4+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
5+
6+
const boo: Foo = { bar: 'bar' };
7+
>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5))
8+
>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0))
9+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 1, 18))
10+
11+
function a(aboo1?: Foo) {
12+
>a : Symbol(a, Decl(controlFlowForIndexSignatures.ts, 1, 32))
13+
>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11))
14+
>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0))
15+
16+
if (!aboo1) return;
17+
>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11))
18+
19+
const aboo2: { [key: string]: typeof aboo1.bar } = boo;
20+
>aboo2 : Symbol(aboo2, Decl(controlFlowForIndexSignatures.ts, 5, 9))
21+
>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 5, 20))
22+
>aboo1.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
23+
>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11))
24+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
25+
>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5))
26+
}
27+
28+
declare let b: Foo | undefined;
29+
>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11))
30+
>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0))
31+
32+
if (b) {
33+
>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11))
34+
35+
const bboo: { [key: string]: typeof b.bar } = boo;
36+
>bboo : Symbol(bboo, Decl(controlFlowForIndexSignatures.ts, 10, 9))
37+
>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 10, 19))
38+
>b.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
39+
>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11))
40+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
41+
>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5))
42+
}
43+
b = boo;
44+
>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11))
45+
>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5))
46+
47+
const bboo: { [key: string]: typeof b.bar } = boo;
48+
>bboo : Symbol(bboo, Decl(controlFlowForIndexSignatures.ts, 13, 5))
49+
>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 13, 15))
50+
>b.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
51+
>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11))
52+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12))
53+
>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5))
54+
55+
declare let c: string | number;
56+
>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11))
57+
58+
if (typeof c === 'string') {
59+
>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11))
60+
61+
type C = { [key: string]: typeof c };
62+
>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28))
63+
>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 17, 16))
64+
>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11))
65+
66+
const boo1: C = { bar: 'works' };
67+
>boo1 : Symbol(boo1, Decl(controlFlowForIndexSignatures.ts, 18, 9))
68+
>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28))
69+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 18, 21))
70+
71+
const boo2: C = { bar: 1 }; // should error
72+
>boo2 : Symbol(boo2, Decl(controlFlowForIndexSignatures.ts, 19, 9))
73+
>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28))
74+
>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 19, 21))
75+
}
76+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
=== tests/cases/compiler/controlFlowForIndexSignatures.ts ===
2+
type Foo = { bar: string };
3+
>Foo : Foo
4+
>bar : string
5+
6+
const boo: Foo = { bar: 'bar' };
7+
>boo : Foo
8+
>{ bar: 'bar' } : { bar: string; }
9+
>bar : string
10+
>'bar' : "bar"
11+
12+
function a(aboo1?: Foo) {
13+
>a : (aboo1?: Foo | undefined) => void
14+
>aboo1 : Foo | undefined
15+
16+
if (!aboo1) return;
17+
>!aboo1 : boolean
18+
>aboo1 : Foo | undefined
19+
20+
const aboo2: { [key: string]: typeof aboo1.bar } = boo;
21+
>aboo2 : { [key: string]: string; }
22+
>key : string
23+
>aboo1.bar : string
24+
>aboo1 : Foo
25+
>bar : string
26+
>boo : Foo
27+
}
28+
29+
declare let b: Foo | undefined;
30+
>b : Foo | undefined
31+
32+
if (b) {
33+
>b : Foo | undefined
34+
35+
const bboo: { [key: string]: typeof b.bar } = boo;
36+
>bboo : { [key: string]: string; }
37+
>key : string
38+
>b.bar : string
39+
>b : Foo
40+
>bar : string
41+
>boo : Foo
42+
}
43+
b = boo;
44+
>b = boo : Foo
45+
>b : Foo | undefined
46+
>boo : Foo
47+
48+
const bboo: { [key: string]: typeof b.bar } = boo;
49+
>bboo : { [key: string]: string; }
50+
>key : string
51+
>b.bar : string
52+
>b : Foo
53+
>bar : string
54+
>boo : Foo
55+
56+
declare let c: string | number;
57+
>c : string | number
58+
59+
if (typeof c === 'string') {
60+
>typeof c === 'string' : boolean
61+
>typeof c : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
62+
>c : string | number
63+
>'string' : "string"
64+
65+
type C = { [key: string]: typeof c };
66+
>C : { [key: string]: string; }
67+
>key : string
68+
>c : string
69+
70+
const boo1: C = { bar: 'works' };
71+
>boo1 : { [key: string]: string; }
72+
>{ bar: 'works' } : { bar: string; }
73+
>bar : string
74+
>'works' : "works"
75+
76+
const boo2: C = { bar: 1 }; // should error
77+
>boo2 : { [key: string]: string; }
78+
>{ bar: 1 } : { bar: number; }
79+
>bar : number
80+
>1 : 1
81+
}
82+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strictNullChecks: true
2+
3+
type Foo = { bar: string };
4+
const boo: Foo = { bar: 'bar' };
5+
6+
function a(aboo1?: Foo) {
7+
if (!aboo1) return;
8+
const aboo2: { [key: string]: typeof aboo1.bar } = boo;
9+
}
10+
11+
declare let b: Foo | undefined;
12+
if (b) {
13+
const bboo: { [key: string]: typeof b.bar } = boo;
14+
}
15+
b = boo;
16+
const bboo: { [key: string]: typeof b.bar } = boo;
17+
18+
declare let c: string | number;
19+
if (typeof c === 'string') {
20+
type C = { [key: string]: typeof c };
21+
const boo1: C = { bar: 'works' };
22+
const boo2: C = { bar: 1 }; // should error
23+
}

0 commit comments

Comments
 (0)