diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0567712f11da3..4a863256da5a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3603,6 +3603,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // constructor functions aren't block scoped return; } + // Labels are in a separate namespace from variables; a label identifier + // that happens to share a name with a block-scoped variable should not + // trigger a "used before its declaration" error. + if (errorLocation.parent) { + const parent = errorLocation.parent; + if ( + parent.kind === SyntaxKind.LabeledStatement && (parent as LabeledStatement).label === errorLocation || + (parent.kind === SyntaxKind.BreakStatement || parent.kind === SyntaxKind.ContinueStatement) && (parent as BreakOrContinueStatement).label === errorLocation + ) { + return; + } + } // Block-scoped variables cannot be used before their definition const declaration = result.declarations?.find( d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration), diff --git a/tests/cases/conformance/statements/labeledStatements/labeledStatementWithBlockScopedVariable.ts b/tests/cases/conformance/statements/labeledStatements/labeledStatementWithBlockScopedVariable.ts new file mode 100644 index 0000000000000..e6548dbb3fc39 --- /dev/null +++ b/tests/cases/conformance/statements/labeledStatements/labeledStatementWithBlockScopedVariable.ts @@ -0,0 +1,25 @@ +// @target: es2015 + +// Labels should not conflict with block-scoped variables declared later. +// A label named `foo` is in a different namespace from a variable named `foo`. + +// Label defined before let variable of the same name +foo: while (true) { + break foo; +} +let foo = 1; + +// Label defined before const variable of the same name +bar: for (;;) { + continue bar; +} +const bar = 2; + +// Nested labeled statement with same-name variable +outer: for (let i = 0; i < 1; i++) { + inner: for (let j = 0; j < 1; j++) { + break outer; + } +} +let outer = "hello"; +let inner = "world";