Skip to content

Commit c07da58

Browse files
authored
Disallow type and interface declarations in statements with blockless bodies (#60183)
1 parent 2e4f2c7 commit c07da58

7 files changed

+364
-3
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46774,6 +46774,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4677446774
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
4677546775
// Grammar checking
4677646776
if (!checkGrammarModifiers(node)) checkGrammarInterfaceDeclaration(node);
46777+
if (!allowBlockDeclarations(node.parent)) {
46778+
grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "interface");
46779+
}
4677746780

4677846781
checkTypeParameters(node.typeParameters);
4677946782
addLazyDiagnostic(() => {
@@ -46817,6 +46820,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4681746820
// Grammar checking
4681846821
checkGrammarModifiers(node);
4681946822
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
46823+
if (!allowBlockDeclarations(node.parent)) {
46824+
grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "type");
46825+
}
4682046826
checkExportsOnMergedDeclarations(node);
4682146827
checkTypeParameters(node.typeParameters);
4682246828
if (node.type.kind === SyntaxKind.IntrinsicKeyword) {
@@ -52051,7 +52057,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5205152057
return false;
5205252058
}
5205352059

52054-
function allowLetAndConstDeclarations(parent: Node): boolean {
52060+
function allowBlockDeclarations(parent: Node): boolean {
5205552061
switch (parent.kind) {
5205652062
case SyntaxKind.IfStatement:
5205752063
case SyntaxKind.DoStatement:
@@ -52062,14 +52068,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5206252068
case SyntaxKind.ForOfStatement:
5206352069
return false;
5206452070
case SyntaxKind.LabeledStatement:
52065-
return allowLetAndConstDeclarations(parent.parent);
52071+
return allowBlockDeclarations(parent.parent);
5206652072
}
5206752073

5206852074
return true;
5206952075
}
5207052076

5207152077
function checkGrammarForDisallowedBlockScopedVariableStatement(node: VariableStatement) {
52072-
if (!allowLetAndConstDeclarations(node.parent)) {
52078+
if (!allowBlockDeclarations(node.parent)) {
5207352079
const blockScopeKind = getCombinedNodeFlagsCached(node.declarationList) & NodeFlags.BlockScoped;
5207452080
if (blockScopeKind) {
5207552081
const keyword = blockScopeKind === NodeFlags.Let ? "let" :
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
typeAliasDeclarationEmit3.ts(3,14): error TS1156: 'type' declarations can only be declared inside a block.
2+
typeAliasDeclarationEmit3.ts(9,14): error TS1156: 'type' declarations can only be declared inside a block.
3+
typeAliasDeclarationEmit3.ts(15,14): error TS1156: 'type' declarations can only be declared inside a block.
4+
5+
6+
==== typeAliasDeclarationEmit3.ts (3 errors) ====
7+
function f1(): void {
8+
for (let i = 0; i < 1; i++)
9+
type foo = [];
10+
~~~
11+
!!! error TS1156: 'type' declarations can only be declared inside a block.
12+
console.log('f1');
13+
}
14+
15+
function f2(): void {
16+
while (true)
17+
type foo = [];
18+
~~~
19+
!!! error TS1156: 'type' declarations can only be declared inside a block.
20+
console.log('f2');
21+
}
22+
23+
function f3(): void {
24+
if (true)
25+
type foo = [];
26+
~~~
27+
!!! error TS1156: 'type' declarations can only be declared inside a block.
28+
console.log('f3');
29+
}
30+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
typeInterfaceDeclarationsInBlockStatements1.ts(4,18): error TS1156: 'type' declarations can only be declared inside a block.
2+
typeInterfaceDeclarationsInBlockStatements1.ts(12,21): error TS2304: Cannot find name 's'.
3+
typeInterfaceDeclarationsInBlockStatements1.ts(17,15): error TS1156: 'interface' declarations can only be declared inside a block.
4+
typeInterfaceDeclarationsInBlockStatements1.ts(29,21): error TS2304: Cannot find name 's'.
5+
6+
7+
==== typeInterfaceDeclarationsInBlockStatements1.ts (4 errors) ====
8+
// https://github.com/microsoft/TypeScript/issues/60175
9+
10+
function f1() {
11+
if (true) type s = string;
12+
~
13+
!!! error TS1156: 'type' declarations can only be declared inside a block.
14+
console.log("" as s);
15+
}
16+
17+
function f2() {
18+
if (true) {
19+
type s = string;
20+
}
21+
console.log("" as s);
22+
~
23+
!!! error TS2304: Cannot find name 's'.
24+
}
25+
26+
function f3() {
27+
if (true)
28+
interface s {
29+
~
30+
!!! error TS1156: 'interface' declarations can only be declared inside a block.
31+
length: number;
32+
}
33+
console.log("" as s);
34+
}
35+
36+
function f4() {
37+
if (true) {
38+
interface s {
39+
length: number;
40+
}
41+
}
42+
console.log("" as s);
43+
~
44+
!!! error TS2304: Cannot find name 's'.
45+
}
46+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////
2+
3+
//// [typeInterfaceDeclarationsInBlockStatements1.ts]
4+
// https://github.com/microsoft/TypeScript/issues/60175
5+
6+
function f1() {
7+
if (true) type s = string;
8+
console.log("" as s);
9+
}
10+
11+
function f2() {
12+
if (true) {
13+
type s = string;
14+
}
15+
console.log("" as s);
16+
}
17+
18+
function f3() {
19+
if (true)
20+
interface s {
21+
length: number;
22+
}
23+
console.log("" as s);
24+
}
25+
26+
function f4() {
27+
if (true) {
28+
interface s {
29+
length: number;
30+
}
31+
}
32+
console.log("" as s);
33+
}
34+
35+
36+
//// [typeInterfaceDeclarationsInBlockStatements1.js]
37+
"use strict";
38+
// https://github.com/microsoft/TypeScript/issues/60175
39+
function f1() {
40+
if (true)
41+
;
42+
console.log("");
43+
}
44+
function f2() {
45+
if (true) {
46+
}
47+
console.log("");
48+
}
49+
function f3() {
50+
if (true)
51+
;
52+
console.log("");
53+
}
54+
function f4() {
55+
if (true) {
56+
}
57+
console.log("");
58+
}
59+
60+
61+
//// [typeInterfaceDeclarationsInBlockStatements1.d.ts]
62+
declare function f1(): void;
63+
declare function f2(): void;
64+
declare function f3(): void;
65+
declare function f4(): void;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////
2+
3+
=== typeInterfaceDeclarationsInBlockStatements1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/60175
5+
6+
function f1() {
7+
>f1 : Symbol(f1, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 0, 0))
8+
9+
if (true) type s = string;
10+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11))
11+
12+
console.log("" as s);
13+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
14+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
15+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
16+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11))
17+
}
18+
19+
function f2() {
20+
>f2 : Symbol(f2, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 5, 1))
21+
22+
if (true) {
23+
type s = string;
24+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 8, 13))
25+
}
26+
console.log("" as s);
27+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
28+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
29+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
30+
>s : Symbol(s)
31+
}
32+
33+
function f3() {
34+
>f3 : Symbol(f3, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 12, 1))
35+
36+
if (true)
37+
interface s {
38+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11))
39+
40+
length: number;
41+
>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 16, 17))
42+
}
43+
console.log("" as s);
44+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
45+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
46+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
47+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11))
48+
}
49+
50+
function f4() {
51+
>f4 : Symbol(f4, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 20, 1))
52+
53+
if (true) {
54+
interface s {
55+
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 23, 13))
56+
57+
length: number;
58+
>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 24, 17))
59+
}
60+
}
61+
console.log("" as s);
62+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
63+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
64+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
65+
>s : Symbol(s)
66+
}
67+
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////
2+
3+
=== typeInterfaceDeclarationsInBlockStatements1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/60175
5+
6+
function f1() {
7+
>f1 : () => void
8+
> : ^^^^^^^^^^
9+
10+
if (true) type s = string;
11+
>true : true
12+
> : ^^^^
13+
>s : string
14+
> : ^^^^^^
15+
16+
console.log("" as s);
17+
>console.log("" as s) : void
18+
> : ^^^^
19+
>console.log : (...data: any[]) => void
20+
> : ^^^^ ^^ ^^^^^
21+
>console : Console
22+
> : ^^^^^^^
23+
>log : (...data: any[]) => void
24+
> : ^^^^ ^^ ^^^^^
25+
>"" as s : string
26+
> : ^^^^^^
27+
>"" : ""
28+
> : ^^
29+
}
30+
31+
function f2() {
32+
>f2 : () => void
33+
> : ^^^^^^^^^^
34+
35+
if (true) {
36+
>true : true
37+
> : ^^^^
38+
39+
type s = string;
40+
>s : string
41+
> : ^^^^^^
42+
}
43+
console.log("" as s);
44+
>console.log("" as s) : void
45+
> : ^^^^
46+
>console.log : (...data: any[]) => void
47+
> : ^^^^ ^^ ^^^^^
48+
>console : Console
49+
> : ^^^^^^^
50+
>log : (...data: any[]) => void
51+
> : ^^^^ ^^ ^^^^^
52+
>"" as s : s
53+
> : ^
54+
>"" : ""
55+
> : ^^
56+
}
57+
58+
function f3() {
59+
>f3 : () => void
60+
> : ^^^^^^^^^^
61+
62+
if (true)
63+
>true : true
64+
> : ^^^^
65+
66+
interface s {
67+
length: number;
68+
>length : number
69+
> : ^^^^^^
70+
}
71+
console.log("" as s);
72+
>console.log("" as s) : void
73+
> : ^^^^
74+
>console.log : (...data: any[]) => void
75+
> : ^^^^ ^^ ^^^^^
76+
>console : Console
77+
> : ^^^^^^^
78+
>log : (...data: any[]) => void
79+
> : ^^^^ ^^ ^^^^^
80+
>"" as s : s
81+
> : ^
82+
>"" : ""
83+
> : ^^
84+
}
85+
86+
function f4() {
87+
>f4 : () => void
88+
> : ^^^^^^^^^^
89+
90+
if (true) {
91+
>true : true
92+
> : ^^^^
93+
94+
interface s {
95+
length: number;
96+
>length : number
97+
> : ^^^^^^
98+
}
99+
}
100+
console.log("" as s);
101+
>console.log("" as s) : void
102+
> : ^^^^
103+
>console.log : (...data: any[]) => void
104+
> : ^^^^ ^^ ^^^^^
105+
>console : Console
106+
> : ^^^^^^^
107+
>log : (...data: any[]) => void
108+
> : ^^^^ ^^ ^^^^^
109+
>"" as s : s
110+
> : ^
111+
>"" : ""
112+
> : ^^
113+
}
114+

0 commit comments

Comments
 (0)