diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 82aa9d1e793..254dbf57532 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11832,9 +11832,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { + return errorType; + } const targetSymbol = resolveAlias(symbol); const exportSymbol = symbol.declarations && getTargetOfAliasDeclaration(getDeclarationOfAliasSymbol(symbol)!, /*dontRecursivelyResolve*/ true); const declaredType = firstDefined(exportSymbol?.declarations, d => isExportAssignment(d) ? tryGetTypeFromEffectiveTypeNode(d) : undefined); + // It only makes sense to get the type of a value symbol. If the result of resolving // the alias is not a value, then it has no type. To get the type associated with a // type symbol, call getDeclaredTypeOfSymbol. @@ -11845,6 +11849,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { : declaredType ? declaredType : getSymbolFlags(targetSymbol) & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) : errorType; + + if (!popTypeResolution()) { + reportCircularityError(exportSymbol ?? symbol); + return links.type = errorType; + } } return links.type; } @@ -11860,15 +11869,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function reportCircularityError(symbol: Symbol) { - const declaration = symbol.valueDeclaration as VariableLikeDeclaration; + const declaration = symbol.valueDeclaration; // Check if variable has type annotation that circularly references the variable itself - if (getEffectiveTypeAnnotationNode(declaration)) { - error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); - return errorType; + if (declaration) { + if (getEffectiveTypeAnnotationNode(declaration)) { + error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); + return errorType; + } + // Check if variable has initializer that circularly references the variable itself + if (noImplicitAny && (declaration.kind !== SyntaxKind.Parameter || (declaration as HasInitializer).initializer)) { + error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, symbolToString(symbol)); + } } - // Check if variable has initializer that circularly references the variable itself - if (noImplicitAny && (declaration.kind !== SyntaxKind.Parameter || (declaration as HasInitializer).initializer)) { - error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, symbolToString(symbol)); + else if (symbol.flags & SymbolFlags.Alias) { + const node = getDeclarationOfAliasSymbol(symbol); + if (node) { + error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol)); + } } // Circularities could also result from parameters in function expressions that end up // having themselves as contextual types following type argument inference. In those cases diff --git a/tests/baselines/reference/pushTypeGetTypeOfAlias.errors.txt b/tests/baselines/reference/pushTypeGetTypeOfAlias.errors.txt new file mode 100644 index 00000000000..126aabf4f24 --- /dev/null +++ b/tests/baselines/reference/pushTypeGetTypeOfAlias.errors.txt @@ -0,0 +1,11 @@ +bar.js(2,1): error TS2303: Circular definition of import alias 'blah'. +bar.js(2,24): error TS2339: Property 'someProp' does not exist on type '{ (): void; blah: any; }'. + + +==== bar.js (2 errors) ==== + module.exports = function () {}; + exports.blah = exports.someProp; + ~~~~~~~~~~~~ +!!! error TS2303: Circular definition of import alias 'blah'. + ~~~~~~~~ +!!! error TS2339: Property 'someProp' does not exist on type '{ (): void; blah: any; }'. \ No newline at end of file diff --git a/tests/baselines/reference/pushTypeGetTypeOfAlias.symbols b/tests/baselines/reference/pushTypeGetTypeOfAlias.symbols new file mode 100644 index 00000000000..a535ccabdea --- /dev/null +++ b/tests/baselines/reference/pushTypeGetTypeOfAlias.symbols @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/pushTypeGetTypeOfAlias.ts] //// + +=== bar.js === +module.exports = function () {}; +>module.exports : Symbol(module.exports, Decl(bar.js, 0, 0)) +>module : Symbol(export=, Decl(bar.js, 0, 0)) +>exports : Symbol(export=, Decl(bar.js, 0, 0)) + +exports.blah = exports.someProp; +>exports.blah : Symbol(blah, Decl(bar.js, 0, 32)) +>exports : Symbol(blah, Decl(bar.js, 0, 32)) +>blah : Symbol(blah, Decl(bar.js, 0, 32)) +>exports : Symbol("bar", Decl(bar.js, 0, 0)) + diff --git a/tests/baselines/reference/pushTypeGetTypeOfAlias.types b/tests/baselines/reference/pushTypeGetTypeOfAlias.types new file mode 100644 index 00000000000..3c5be072c65 --- /dev/null +++ b/tests/baselines/reference/pushTypeGetTypeOfAlias.types @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/pushTypeGetTypeOfAlias.ts] //// + +=== bar.js === +module.exports = function () {}; +>module.exports = function () {} : { (): void; blah: any; } +>module.exports : { (): void; blah: any; } +>module : { exports: { (): void; blah: any; }; } +>exports : { (): void; blah: any; } +>function () {} : () => void + +exports.blah = exports.someProp; +>exports.blah = exports.someProp : any +>exports.blah : any +>exports : { (): void; blah: any; } +>blah : any +>exports.someProp : any +>exports : { (): void; blah: any; } +>someProp : any + diff --git a/tests/baselines/reference/recursiveExportAssignmentAndFindAliasedType7.types b/tests/baselines/reference/recursiveExportAssignmentAndFindAliasedType7.types index a011075ca94..be2f30d8efd 100644 --- a/tests/baselines/reference/recursiveExportAssignmentAndFindAliasedType7.types +++ b/tests/baselines/reference/recursiveExportAssignmentAndFindAliasedType7.types @@ -16,7 +16,7 @@ import self = require("recursiveExportAssignmentAndFindAliasedType7_moduleD"); var selfVar = self; >selfVar : any ->self : any +>self : error export = selfVar; >selfVar : any diff --git a/tests/cases/compiler/pushTypeGetTypeOfAlias.ts b/tests/cases/compiler/pushTypeGetTypeOfAlias.ts new file mode 100644 index 00000000000..0092bf6cca5 --- /dev/null +++ b/tests/cases/compiler/pushTypeGetTypeOfAlias.ts @@ -0,0 +1,7 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @Filename: bar.js +module.exports = function () {}; +exports.blah = exports.someProp; \ No newline at end of file