From b18141b0bca0ebe5311cf0288e4014f5e65283ed Mon Sep 17 00:00:00 2001 From: jihndai <73680880+jihndai@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:29:07 -0400 Subject: [PATCH] Do not reset control flow analysis on index signatures (#48427) * do not flag index signatures as a control flow container * add tests and baselines --- src/compiler/binder.ts | 2 +- .../controlFlowForIndexSignatures.errors.txt | 29 +++++++ .../controlFlowForIndexSignatures.js | 40 +++++++++ .../controlFlowForIndexSignatures.symbols | 76 +++++++++++++++++ .../controlFlowForIndexSignatures.types | 82 +++++++++++++++++++ .../compiler/controlFlowForIndexSignatures.ts | 23 ++++++ 6 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/controlFlowForIndexSignatures.errors.txt create mode 100644 tests/baselines/reference/controlFlowForIndexSignatures.js create mode 100644 tests/baselines/reference/controlFlowForIndexSignatures.symbols create mode 100644 tests/baselines/reference/controlFlowForIndexSignatures.types create mode 100644 tests/cases/compiler/controlFlowForIndexSignatures.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a5dc3db10bc..66a9f6393dc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1818,6 +1818,7 @@ namespace ts { case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.MappedType: + case SyntaxKind.IndexSignature: return ContainerFlags.IsContainer | ContainerFlags.HasLocals; case SyntaxKind.SourceFile: @@ -1838,7 +1839,6 @@ namespace ts { case SyntaxKind.JSDocFunctionType: case SyntaxKind.FunctionType: case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: case SyntaxKind.ConstructorType: case SyntaxKind.ClassStaticBlockDeclaration: return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; diff --git a/tests/baselines/reference/controlFlowForIndexSignatures.errors.txt b/tests/baselines/reference/controlFlowForIndexSignatures.errors.txt new file mode 100644 index 00000000000..aedc120c1c9 --- /dev/null +++ b/tests/baselines/reference/controlFlowForIndexSignatures.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/controlFlowForIndexSignatures.ts(20,23): error TS2322: Type 'number' is not assignable to type 'string'. + + +==== tests/cases/compiler/controlFlowForIndexSignatures.ts (1 errors) ==== + type Foo = { bar: string }; + const boo: Foo = { bar: 'bar' }; + + function a(aboo1?: Foo) { + if (!aboo1) return; + const aboo2: { [key: string]: typeof aboo1.bar } = boo; + } + + declare let b: Foo | undefined; + if (b) { + const bboo: { [key: string]: typeof b.bar } = boo; + } + b = boo; + const bboo: { [key: string]: typeof b.bar } = boo; + + declare let c: string | number; + if (typeof c === 'string') { + type C = { [key: string]: typeof c }; + const boo1: C = { bar: 'works' }; + const boo2: C = { bar: 1 }; // should error + ~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! related TS6501 tests/cases/compiler/controlFlowForIndexSignatures.ts:18:16: The expected type comes from this index signature. + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowForIndexSignatures.js b/tests/baselines/reference/controlFlowForIndexSignatures.js new file mode 100644 index 00000000000..c63ff537d00 --- /dev/null +++ b/tests/baselines/reference/controlFlowForIndexSignatures.js @@ -0,0 +1,40 @@ +//// [controlFlowForIndexSignatures.ts] +type Foo = { bar: string }; +const boo: Foo = { bar: 'bar' }; + +function a(aboo1?: Foo) { + if (!aboo1) return; + const aboo2: { [key: string]: typeof aboo1.bar } = boo; +} + +declare let b: Foo | undefined; +if (b) { + const bboo: { [key: string]: typeof b.bar } = boo; +} +b = boo; +const bboo: { [key: string]: typeof b.bar } = boo; + +declare let c: string | number; +if (typeof c === 'string') { + type C = { [key: string]: typeof c }; + const boo1: C = { bar: 'works' }; + const boo2: C = { bar: 1 }; // should error +} + + +//// [controlFlowForIndexSignatures.js] +var boo = { bar: 'bar' }; +function a(aboo1) { + if (!aboo1) + return; + var aboo2 = boo; +} +if (b) { + var bboo_1 = boo; +} +b = boo; +var bboo = boo; +if (typeof c === 'string') { + var boo1 = { bar: 'works' }; + var boo2 = { bar: 1 }; // should error +} diff --git a/tests/baselines/reference/controlFlowForIndexSignatures.symbols b/tests/baselines/reference/controlFlowForIndexSignatures.symbols new file mode 100644 index 00000000000..f2b71e58f08 --- /dev/null +++ b/tests/baselines/reference/controlFlowForIndexSignatures.symbols @@ -0,0 +1,76 @@ +=== tests/cases/compiler/controlFlowForIndexSignatures.ts === +type Foo = { bar: string }; +>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) + +const boo: Foo = { bar: 'bar' }; +>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5)) +>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 1, 18)) + +function a(aboo1?: Foo) { +>a : Symbol(a, Decl(controlFlowForIndexSignatures.ts, 1, 32)) +>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11)) +>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0)) + + if (!aboo1) return; +>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11)) + + const aboo2: { [key: string]: typeof aboo1.bar } = boo; +>aboo2 : Symbol(aboo2, Decl(controlFlowForIndexSignatures.ts, 5, 9)) +>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 5, 20)) +>aboo1.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>aboo1 : Symbol(aboo1, Decl(controlFlowForIndexSignatures.ts, 3, 11)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5)) +} + +declare let b: Foo | undefined; +>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11)) +>Foo : Symbol(Foo, Decl(controlFlowForIndexSignatures.ts, 0, 0)) + +if (b) { +>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11)) + + const bboo: { [key: string]: typeof b.bar } = boo; +>bboo : Symbol(bboo, Decl(controlFlowForIndexSignatures.ts, 10, 9)) +>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 10, 19)) +>b.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5)) +} +b = boo; +>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11)) +>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5)) + +const bboo: { [key: string]: typeof b.bar } = boo; +>bboo : Symbol(bboo, Decl(controlFlowForIndexSignatures.ts, 13, 5)) +>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 13, 15)) +>b.bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>b : Symbol(b, Decl(controlFlowForIndexSignatures.ts, 8, 11)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 0, 12)) +>boo : Symbol(boo, Decl(controlFlowForIndexSignatures.ts, 1, 5)) + +declare let c: string | number; +>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11)) + +if (typeof c === 'string') { +>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11)) + + type C = { [key: string]: typeof c }; +>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28)) +>key : Symbol(key, Decl(controlFlowForIndexSignatures.ts, 17, 16)) +>c : Symbol(c, Decl(controlFlowForIndexSignatures.ts, 15, 11)) + + const boo1: C = { bar: 'works' }; +>boo1 : Symbol(boo1, Decl(controlFlowForIndexSignatures.ts, 18, 9)) +>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 18, 21)) + + const boo2: C = { bar: 1 }; // should error +>boo2 : Symbol(boo2, Decl(controlFlowForIndexSignatures.ts, 19, 9)) +>C : Symbol(C, Decl(controlFlowForIndexSignatures.ts, 16, 28)) +>bar : Symbol(bar, Decl(controlFlowForIndexSignatures.ts, 19, 21)) +} + diff --git a/tests/baselines/reference/controlFlowForIndexSignatures.types b/tests/baselines/reference/controlFlowForIndexSignatures.types new file mode 100644 index 00000000000..eb6757f0d3f --- /dev/null +++ b/tests/baselines/reference/controlFlowForIndexSignatures.types @@ -0,0 +1,82 @@ +=== tests/cases/compiler/controlFlowForIndexSignatures.ts === +type Foo = { bar: string }; +>Foo : Foo +>bar : string + +const boo: Foo = { bar: 'bar' }; +>boo : Foo +>{ bar: 'bar' } : { bar: string; } +>bar : string +>'bar' : "bar" + +function a(aboo1?: Foo) { +>a : (aboo1?: Foo | undefined) => void +>aboo1 : Foo | undefined + + if (!aboo1) return; +>!aboo1 : boolean +>aboo1 : Foo | undefined + + const aboo2: { [key: string]: typeof aboo1.bar } = boo; +>aboo2 : { [key: string]: string; } +>key : string +>aboo1.bar : string +>aboo1 : Foo +>bar : string +>boo : Foo +} + +declare let b: Foo | undefined; +>b : Foo | undefined + +if (b) { +>b : Foo | undefined + + const bboo: { [key: string]: typeof b.bar } = boo; +>bboo : { [key: string]: string; } +>key : string +>b.bar : string +>b : Foo +>bar : string +>boo : Foo +} +b = boo; +>b = boo : Foo +>b : Foo | undefined +>boo : Foo + +const bboo: { [key: string]: typeof b.bar } = boo; +>bboo : { [key: string]: string; } +>key : string +>b.bar : string +>b : Foo +>bar : string +>boo : Foo + +declare let c: string | number; +>c : string | number + +if (typeof c === 'string') { +>typeof c === 'string' : boolean +>typeof c : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>c : string | number +>'string' : "string" + + type C = { [key: string]: typeof c }; +>C : { [key: string]: string; } +>key : string +>c : string + + const boo1: C = { bar: 'works' }; +>boo1 : { [key: string]: string; } +>{ bar: 'works' } : { bar: string; } +>bar : string +>'works' : "works" + + const boo2: C = { bar: 1 }; // should error +>boo2 : { [key: string]: string; } +>{ bar: 1 } : { bar: number; } +>bar : number +>1 : 1 +} + diff --git a/tests/cases/compiler/controlFlowForIndexSignatures.ts b/tests/cases/compiler/controlFlowForIndexSignatures.ts new file mode 100644 index 00000000000..d2129f74c2e --- /dev/null +++ b/tests/cases/compiler/controlFlowForIndexSignatures.ts @@ -0,0 +1,23 @@ +// @strictNullChecks: true + +type Foo = { bar: string }; +const boo: Foo = { bar: 'bar' }; + +function a(aboo1?: Foo) { + if (!aboo1) return; + const aboo2: { [key: string]: typeof aboo1.bar } = boo; +} + +declare let b: Foo | undefined; +if (b) { + const bboo: { [key: string]: typeof b.bar } = boo; +} +b = boo; +const bboo: { [key: string]: typeof b.bar } = boo; + +declare let c: string | number; +if (typeof c === 'string') { + type C = { [key: string]: typeof c }; + const boo1: C = { bar: 'works' }; + const boo2: C = { bar: 1 }; // should error +}