Do not reset control flow analysis on index signatures (#48427)

* do not flag index signatures as a control flow container

* add tests and baselines
This commit is contained in:
jihndai 2022-04-07 16:29:07 -04:00 committed by GitHub
parent b975dfa102
commit b18141b0bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 251 additions and 1 deletions

View File

@ -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;

View File

@ -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.
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}