diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 83fea11c993..57905315d73 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1056,9 +1056,9 @@ namespace ts { // block-scoped variable and namespace module. However, only when we // try to resolve name in /*1*/ which is used in variable position, // we want to check for block-scoped - if (meaning & SymbolFlags.BlockScopedVariable) { + if (meaning & SymbolFlags.BlockScopedVariable || (meaning & SymbolFlags.Class && (meaning & SymbolFlags.Value) === SymbolFlags.Value)) { const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result); - if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable) { + if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class) { checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation); } } @@ -1171,14 +1171,19 @@ namespace ts { } function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { - Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0); + Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class)); // Block-scoped variables cannot be used before their definition - const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined); + const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) || isClassLike(d) ? d : undefined); Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) { - error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); + if (result.flags & SymbolFlags.BlockScopedVariable) { + error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); + } + else { + error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(declaration.name)); + } } } @@ -13171,10 +13176,17 @@ namespace ts { } return unknownType; } - if (prop.valueDeclaration && - isInPropertyInitializer(node) && - !isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) { - error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text); + if (prop.valueDeclaration) { + if (isInPropertyInitializer(node) && + !isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) { + error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text); + } + if (prop.valueDeclaration.kind === SyntaxKind.ClassDeclaration && + node.parent && node.parent.kind !== SyntaxKind.TypeReference && + !isInAmbientContext(prop.valueDeclaration) && + !isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) { + error(right, Diagnostics.Class_0_used_before_its_declaration, right.text); + } } markPropertyAsReferenced(prop); @@ -19421,14 +19433,6 @@ namespace ts { error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any); } - if (baseType.symbol && baseType.symbol.valueDeclaration && - !isInAmbientContext(baseType.symbol.valueDeclaration) && - baseType.symbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration) { - if (!isBlockScopedNameDeclaredBeforeUse(baseType.symbol.valueDeclaration, node)) { - error(baseTypeNode, Diagnostics.A_class_must_be_declared_after_its_base_class); - } - } - if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) { // When the static base type is a "class-like" constructor function (but not actually a class), we verify // that all instantiated base constructor signatures return the same type. We can simply compare the type diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b9572b2b0f4..691f8e8d31b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1435,6 +1435,10 @@ "category": "Error", "code": 2448 }, + "Class '{0}' used before its declaration.": { + "category": "Error", + "code": 2449 + }, "Cannot redeclare block-scoped variable '{0}'.": { "category": "Error", "code": 2451 @@ -2019,10 +2023,6 @@ "category": "Error", "code": 2689 }, - "A class must be declared after its base class.": { - "category": "Error", - "code": 2690 - }, "An import path cannot end with a '{0}' extension. Consider importing '{1}' instead.": { "category": "Error", "code": 2691