diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7750d8af73..5658109668 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2448,10 +2448,11 @@ namespace ts { } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration | VariableDeclaration, dontResolveAlias: boolean): Symbol | undefined { - if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) { - const name = (getLeftmostAccessExpression(node.initializer.expression) as CallExpression).arguments[0] as StringLiteral; - return isIdentifier(node.initializer.name) - ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), node.initializer.name.escapedText)) + const commonJSPropertyAccess = getCommonJSPropertyAccess(node); + if (commonJSPropertyAccess) { + const name = (getLeftmostAccessExpression(commonJSPropertyAccess.expression) as CallExpression).arguments[0] as StringLiteral; + return isIdentifier(commonJSPropertyAccess.name) + ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), commonJSPropertyAccess.name.escapedText)) : undefined; } if (isVariableDeclaration(node) || node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { @@ -2646,12 +2647,8 @@ namespace ts { return result; } - function getExportOfModule(symbol: Symbol, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { + function getExportOfModule(symbol: Symbol, name: Identifier, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined { if (symbol.flags & SymbolFlags.Module) { - const name = specifier.propertyName ?? specifier.name; - if (!isIdentifier(name)) { - return undefined; - } const exportSymbol = getExportsOfSymbol(symbol).get(name.escapedText); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false); @@ -2668,10 +2665,10 @@ namespace ts { } } - function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias = false): Symbol | undefined { + function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined { const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration).moduleSpecifier!; const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217 - const name = specifier.propertyName || specifier.name; + const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name; if (!isIdentifier(name)) { return undefined; } @@ -2691,10 +2688,10 @@ namespace ts { else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText); } - // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); - let symbolFromModule = getExportOfModule(targetSymbol, specifier, dontResolveAlias); + + let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias); if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) { const file = find(moduleSymbol.declarations, isSourceFile); if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) { @@ -2782,11 +2779,23 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { - const resolved = getExternalModuleMember(isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent, node, dontResolveAlias); + const root = isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent; + const commonJSPropertyAccess = getCommonJSPropertyAccess(root); + const resolved = getExternalModuleMember(root, commonJSPropertyAccess || node, dontResolveAlias); + const name = node.propertyName || node.name; + if (commonJSPropertyAccess && resolved && isIdentifier(name)) { + return getPropertyOfType(getTypeOfSymbol(resolved), name.escapedText); + } markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } + function getCommonJSPropertyAccess(node: Node) { + if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) { + return node.initializer; + } + } + function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol { const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); @@ -2940,7 +2949,7 @@ namespace ts { finalTarget: Symbol | undefined, overwriteEmpty: boolean, ): boolean { - if (!aliasDeclaration) return false; + if (!aliasDeclaration || isPropertyAccessExpression(aliasDeclaration)) return false; // If the declaration itself is type-only, mark it and return. // No need to check what it resolves to. diff --git a/tests/baselines/reference/jsDeclarationsTypeReferences2.errors.txt b/tests/baselines/reference/jsDeclarationsTypeReferences2.errors.txt deleted file mode 100644 index e16b5cca80..0000000000 --- a/tests/baselines/reference/jsDeclarationsTypeReferences2.errors.txt +++ /dev/null @@ -1,23 +0,0 @@ -tests/cases/conformance/jsdoc/declarations/index.js(1,8): error TS2305: Module '"./something"' has no exported member 'a'. -tests/cases/conformance/jsdoc/declarations/index.js(1,11): error TS2305: Module '"./something"' has no exported member 'm'. - - -==== tests/cases/conformance/jsdoc/declarations/index.js (2 errors) ==== - const{ a, m } = require("./something").o; - ~ -!!! error TS2305: Module '"./something"' has no exported member 'a'. - ~ -!!! error TS2305: Module '"./something"' has no exported member 'm'. - - const thing = a + m - - module.exports = { - thing - }; - -==== tests/cases/conformance/jsdoc/declarations/something.ts (0 errors) ==== - export const o = { - a: 1, - m: 1 - } - \ No newline at end of file diff --git a/tests/baselines/reference/jsDeclarationsTypeReferences2.js b/tests/baselines/reference/jsDeclarationsTypeReferences2.js index a38563cf37..0f219af391 100644 --- a/tests/baselines/reference/jsDeclarationsTypeReferences2.js +++ b/tests/baselines/reference/jsDeclarationsTypeReferences2.js @@ -38,4 +38,4 @@ export declare const o: { m: number; }; //// [index.d.ts] -export const thing: any; +export const thing: number; diff --git a/tests/baselines/reference/jsDeclarationsTypeReferences2.types b/tests/baselines/reference/jsDeclarationsTypeReferences2.types index e57c2b57c6..59eda0c19d 100644 --- a/tests/baselines/reference/jsDeclarationsTypeReferences2.types +++ b/tests/baselines/reference/jsDeclarationsTypeReferences2.types @@ -1,7 +1,7 @@ === tests/cases/conformance/jsdoc/declarations/index.js === const{ a, m } = require("./something").o; ->a : any ->m : any +>a : number +>m : number >require("./something").o : { a: number; m: number; } >require("./something") : typeof import("tests/cases/conformance/jsdoc/declarations/something") >require : any @@ -9,20 +9,20 @@ const{ a, m } = require("./something").o; >o : { a: number; m: number; } const thing = a + m ->thing : any ->a + m : any ->a : any ->m : any +>thing : number +>a + m : number +>a : number +>m : number module.exports = { ->module.exports = { thing} : { thing: any; } ->module.exports : { thing: any; } ->module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": { thing: any; }; } ->exports : { thing: any; } ->{ thing} : { thing: any; } +>module.exports = { thing} : { thing: number; } +>module.exports : { thing: number; } +>module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": { thing: number; }; } +>exports : { thing: number; } +>{ thing} : { thing: number; } thing ->thing : any +>thing : number };