Skip to content

Disallow type and interface declarations in statements with blockless bodies #60183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
@@ -46768,6 +46768,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
if (!checkGrammarModifiers(node)) checkGrammarInterfaceDeclaration(node);
if (!allowBlockDeclarations(node.parent)) {
grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "interface");
}

checkTypeParameters(node.typeParameters);
addLazyDiagnostic(() => {
@@ -46811,6 +46814,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// Grammar checking
checkGrammarModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
if (!allowBlockDeclarations(node.parent)) {
grammarErrorOnNode(node, Diagnostics._0_declarations_can_only_be_declared_inside_a_block, "type");
}
checkExportsOnMergedDeclarations(node);
checkTypeParameters(node.typeParameters);
if (node.type.kind === SyntaxKind.IntrinsicKeyword) {
@@ -52042,7 +52048,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}

function allowLetAndConstDeclarations(parent: Node): boolean {
function allowBlockDeclarations(parent: Node): boolean {
switch (parent.kind) {
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
@@ -52053,14 +52059,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
case SyntaxKind.ForOfStatement:
return false;
case SyntaxKind.LabeledStatement:
return allowLetAndConstDeclarations(parent.parent);
return allowBlockDeclarations(parent.parent);
}

return true;
}

function checkGrammarForDisallowedBlockScopedVariableStatement(node: VariableStatement) {
if (!allowLetAndConstDeclarations(node.parent)) {
if (!allowBlockDeclarations(node.parent)) {
const blockScopeKind = getCombinedNodeFlagsCached(node.declarationList) & NodeFlags.BlockScoped;
if (blockScopeKind) {
const keyword = blockScopeKind === NodeFlags.Let ? "let" :
30 changes: 30 additions & 0 deletions tests/baselines/reference/typeAliasDeclarationEmit3.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
typeAliasDeclarationEmit3.ts(3,14): error TS1156: 'type' declarations can only be declared inside a block.
typeAliasDeclarationEmit3.ts(9,14): error TS1156: 'type' declarations can only be declared inside a block.
typeAliasDeclarationEmit3.ts(15,14): error TS1156: 'type' declarations can only be declared inside a block.


==== typeAliasDeclarationEmit3.ts (3 errors) ====
function f1(): void {
for (let i = 0; i < 1; i++)
type foo = [];
~~~
!!! error TS1156: 'type' declarations can only be declared inside a block.
console.log('f1');
}

function f2(): void {
while (true)
type foo = [];
~~~
!!! error TS1156: 'type' declarations can only be declared inside a block.
console.log('f2');
}

function f3(): void {
if (true)
type foo = [];
~~~
!!! error TS1156: 'type' declarations can only be declared inside a block.
console.log('f3');
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
typeInterfaceDeclarationsInBlockStatements1.ts(4,18): error TS1156: 'type' declarations can only be declared inside a block.
typeInterfaceDeclarationsInBlockStatements1.ts(12,21): error TS2304: Cannot find name 's'.
typeInterfaceDeclarationsInBlockStatements1.ts(17,15): error TS1156: 'interface' declarations can only be declared inside a block.
typeInterfaceDeclarationsInBlockStatements1.ts(29,21): error TS2304: Cannot find name 's'.


==== typeInterfaceDeclarationsInBlockStatements1.ts (4 errors) ====
// https://github.com/microsoft/TypeScript/issues/60175

function f1() {
if (true) type s = string;
~
!!! error TS1156: 'type' declarations can only be declared inside a block.
console.log("" as s);
}

function f2() {
if (true) {
type s = string;
}
console.log("" as s);
~
!!! error TS2304: Cannot find name 's'.
}

function f3() {
if (true)
interface s {
~
!!! error TS1156: 'interface' declarations can only be declared inside a block.
length: number;
}
console.log("" as s);
}

function f4() {
if (true) {
interface s {
length: number;
}
}
console.log("" as s);
~
!!! error TS2304: Cannot find name 's'.
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////

//// [typeInterfaceDeclarationsInBlockStatements1.ts]
// https://github.com/microsoft/TypeScript/issues/60175

function f1() {
if (true) type s = string;
console.log("" as s);
}

function f2() {
if (true) {
type s = string;
}
console.log("" as s);
}

function f3() {
if (true)
interface s {
length: number;
}
console.log("" as s);
}

function f4() {
if (true) {
interface s {
length: number;
}
}
console.log("" as s);
}


//// [typeInterfaceDeclarationsInBlockStatements1.js]
"use strict";
// https://github.com/microsoft/TypeScript/issues/60175
function f1() {
if (true)
;
console.log("");
}
function f2() {
if (true) {
}
console.log("");
}
function f3() {
if (true)
;
console.log("");
}
function f4() {
if (true) {
}
console.log("");
}


//// [typeInterfaceDeclarationsInBlockStatements1.d.ts]
declare function f1(): void;
declare function f2(): void;
declare function f3(): void;
declare function f4(): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////

=== typeInterfaceDeclarationsInBlockStatements1.ts ===
// https://github.com/microsoft/TypeScript/issues/60175

function f1() {
>f1 : Symbol(f1, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 0, 0))

if (true) type s = string;
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11))

console.log("" as s);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 3, 11))
}

function f2() {
>f2 : Symbol(f2, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 5, 1))

if (true) {
type s = string;
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 8, 13))
}
console.log("" as s);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>s : Symbol(s)
}

function f3() {
>f3 : Symbol(f3, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 12, 1))

if (true)
interface s {
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11))

length: number;
>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 16, 17))
}
console.log("" as s);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 15, 11))
}

function f4() {
>f4 : Symbol(f4, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 20, 1))

if (true) {
interface s {
>s : Symbol(s, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 23, 13))

length: number;
>length : Symbol(s.length, Decl(typeInterfaceDeclarationsInBlockStatements1.ts, 24, 17))
}
}
console.log("" as s);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>s : Symbol(s)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//// [tests/cases/compiler/typeInterfaceDeclarationsInBlockStatements1.ts] ////

=== typeInterfaceDeclarationsInBlockStatements1.ts ===
// https://github.com/microsoft/TypeScript/issues/60175

function f1() {
>f1 : () => void
> : ^^^^^^^^^^

if (true) type s = string;
>true : true
> : ^^^^
>s : string
> : ^^^^^^

console.log("" as s);
>console.log("" as s) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"" as s : string
> : ^^^^^^
>"" : ""
> : ^^
}

function f2() {
>f2 : () => void
> : ^^^^^^^^^^

if (true) {
>true : true
> : ^^^^

type s = string;
>s : string
> : ^^^^^^
}
console.log("" as s);
>console.log("" as s) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"" as s : s
> : ^
>"" : ""
> : ^^
}

function f3() {
>f3 : () => void
> : ^^^^^^^^^^

if (true)
>true : true
> : ^^^^

interface s {
length: number;
>length : number
> : ^^^^^^
}
console.log("" as s);
>console.log("" as s) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"" as s : s
> : ^
>"" : ""
> : ^^
}

function f4() {
>f4 : () => void
> : ^^^^^^^^^^

if (true) {
>true : true
> : ^^^^

interface s {
length: number;
>length : number
> : ^^^^^^
}
}
console.log("" as s);
>console.log("" as s) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>"" as s : s
> : ^
>"" : ""
> : ^^
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// @strict: true
// @declaration: true

// https://github.com/microsoft/TypeScript/issues/60175

function f1() {
if (true) type s = string;
console.log("" as s);
}

function f2() {
if (true) {
type s = string;
}
console.log("" as s);
}

function f3() {
if (true)
interface s {
length: number;
}
console.log("" as s);
}

function f4() {
if (true) {
interface s {
length: number;
}
}
console.log("" as s);
}