Skip to content

Commit ed7a3d0

Browse files
author
Kanchalai Tanglertsampan
committed
Issue an error when class is used before class declaration
1 parent 65da012 commit ed7a3d0

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,9 +1056,9 @@ namespace ts {
10561056
// block-scoped variable and namespace module. However, only when we
10571057
// try to resolve name in /*1*/ which is used in variable position,
10581058
// we want to check for block-scoped
1059-
if (meaning & SymbolFlags.BlockScopedVariable) {
1059+
if (meaning & SymbolFlags.BlockScopedVariable || (meaning & SymbolFlags.Class && (meaning & SymbolFlags.Value) === SymbolFlags.Value)) {
10601060
const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
1061-
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable) {
1061+
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class) {
10621062
checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
10631063
}
10641064
}
@@ -1171,14 +1171,19 @@ namespace ts {
11711171
}
11721172

11731173
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
1174-
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0);
1174+
Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class));
11751175
// Block-scoped variables cannot be used before their definition
1176-
const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined);
1176+
const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) || isClassLike(d) ? d : undefined);
11771177

11781178
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
11791179

11801180
if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
1181-
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
1181+
if (result.flags & SymbolFlags.BlockScopedVariable) {
1182+
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
1183+
}
1184+
else {
1185+
error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(declaration.name));
1186+
}
11821187
}
11831188
}
11841189

@@ -13171,10 +13176,17 @@ namespace ts {
1317113176
}
1317213177
return unknownType;
1317313178
}
13174-
if (prop.valueDeclaration &&
13175-
isInPropertyInitializer(node) &&
13176-
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
13177-
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text);
13179+
if (prop.valueDeclaration) {
13180+
if (isInPropertyInitializer(node) &&
13181+
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
13182+
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text);
13183+
}
13184+
if (prop.valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
13185+
node.parent && node.parent.kind !== SyntaxKind.TypeReference &&
13186+
!isInAmbientContext(prop.valueDeclaration) &&
13187+
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
13188+
error(right, Diagnostics.Class_0_used_before_its_declaration, right.text);
13189+
}
1317813190
}
1317913191

1318013192
markPropertyAsReferenced(prop);
@@ -19421,14 +19433,6 @@ namespace ts {
1942119433
error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any);
1942219434
}
1942319435

19424-
if (baseType.symbol && baseType.symbol.valueDeclaration &&
19425-
!isInAmbientContext(baseType.symbol.valueDeclaration) &&
19426-
baseType.symbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration) {
19427-
if (!isBlockScopedNameDeclaredBeforeUse(baseType.symbol.valueDeclaration, node)) {
19428-
error(baseTypeNode, Diagnostics.A_class_must_be_declared_after_its_base_class);
19429-
}
19430-
}
19431-
1943219436
if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) {
1943319437
// When the static base type is a "class-like" constructor function (but not actually a class), we verify
1943419438
// that all instantiated base constructor signatures return the same type. We can simply compare the type

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,10 @@
14351435
"category": "Error",
14361436
"code": 2448
14371437
},
1438+
"Class '{0}' used before its declaration.": {
1439+
"category": "Error",
1440+
"code": 2449
1441+
},
14381442
"Cannot redeclare block-scoped variable '{0}'.": {
14391443
"category": "Error",
14401444
"code": 2451
@@ -2019,10 +2023,6 @@
20192023
"category": "Error",
20202024
"code": 2689
20212025
},
2022-
"A class must be declared after its base class.": {
2023-
"category": "Error",
2024-
"code": 2690
2025-
},
20262026
"An import path cannot end with a '{0}' extension. Consider importing '{1}' instead.": {
20272027
"category": "Error",
20282028
"code": 2691

0 commit comments

Comments
 (0)