Skip to content

Commit eac75f3

Browse files
authored
CommonJS imports support destructuring+property access (#40702)
* CommonJS imports support destructuring+property access Fixes #40578 for prettier * will I ever remember semicolons? haha no * move code around * move function declaration closer to use * Add missing space after `if` Thanks to @weswigham for noticing this. Somehow it passed the linter.
1 parent e6fdcce commit eac75f3

File tree

4 files changed

+37
-51
lines changed

4 files changed

+37
-51
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,10 +2448,11 @@ namespace ts {
24482448
}
24492449

24502450
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration | VariableDeclaration, dontResolveAlias: boolean): Symbol | undefined {
2451-
if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) {
2452-
const name = (getLeftmostAccessExpression(node.initializer.expression) as CallExpression).arguments[0] as StringLiteral;
2453-
return isIdentifier(node.initializer.name)
2454-
? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), node.initializer.name.escapedText))
2451+
const commonJSPropertyAccess = getCommonJSPropertyAccess(node);
2452+
if (commonJSPropertyAccess) {
2453+
const name = (getLeftmostAccessExpression(commonJSPropertyAccess.expression) as CallExpression).arguments[0] as StringLiteral;
2454+
return isIdentifier(commonJSPropertyAccess.name)
2455+
? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), commonJSPropertyAccess.name.escapedText))
24552456
: undefined;
24562457
}
24572458
if (isVariableDeclaration(node) || node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
@@ -2646,12 +2647,8 @@ namespace ts {
26462647
return result;
26472648
}
26482649

2649-
function getExportOfModule(symbol: Symbol, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined {
2650+
function getExportOfModule(symbol: Symbol, name: Identifier, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined {
26502651
if (symbol.flags & SymbolFlags.Module) {
2651-
const name = specifier.propertyName ?? specifier.name;
2652-
if (!isIdentifier(name)) {
2653-
return undefined;
2654-
}
26552652
const exportSymbol = getExportsOfSymbol(symbol).get(name.escapedText);
26562653
const resolved = resolveSymbol(exportSymbol, dontResolveAlias);
26572654
markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false);
@@ -2668,10 +2665,10 @@ namespace ts {
26682665
}
26692666
}
26702667

2671-
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement, dontResolveAlias = false): Symbol | undefined {
2668+
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined {
26722669
const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration).moduleSpecifier!;
26732670
const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217
2674-
const name = specifier.propertyName || specifier.name;
2671+
const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name;
26752672
if (!isIdentifier(name)) {
26762673
return undefined;
26772674
}
@@ -2691,10 +2688,10 @@ namespace ts {
26912688
else {
26922689
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText);
26932690
}
2694-
26952691
// if symbolFromVariable is export - get its final target
26962692
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
2697-
let symbolFromModule = getExportOfModule(targetSymbol, specifier, dontResolveAlias);
2693+
2694+
let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias);
26982695
if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) {
26992696
const file = find(moduleSymbol.declarations, isSourceFile);
27002697
if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) {
@@ -2782,11 +2779,23 @@ namespace ts {
27822779
}
27832780

27842781
function getTargetOfImportSpecifier(node: ImportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined {
2785-
const resolved = getExternalModuleMember(isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent, node, dontResolveAlias);
2782+
const root = isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent;
2783+
const commonJSPropertyAccess = getCommonJSPropertyAccess(root);
2784+
const resolved = getExternalModuleMember(root, commonJSPropertyAccess || node, dontResolveAlias);
2785+
const name = node.propertyName || node.name;
2786+
if (commonJSPropertyAccess && resolved && isIdentifier(name)) {
2787+
return getPropertyOfType(getTypeOfSymbol(resolved), name.escapedText);
2788+
}
27862789
markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false);
27872790
return resolved;
27882791
}
27892792

2793+
function getCommonJSPropertyAccess(node: Node) {
2794+
if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) {
2795+
return node.initializer;
2796+
}
2797+
}
2798+
27902799
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
27912800
const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
27922801
markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false);
@@ -2940,7 +2949,7 @@ namespace ts {
29402949
finalTarget: Symbol | undefined,
29412950
overwriteEmpty: boolean,
29422951
): boolean {
2943-
if (!aliasDeclaration) return false;
2952+
if (!aliasDeclaration || isPropertyAccessExpression(aliasDeclaration)) return false;
29442953

29452954
// If the declaration itself is type-only, mark it and return.
29462955
// No need to check what it resolves to.

tests/baselines/reference/jsDeclarationsTypeReferences2.errors.txt

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/baselines/reference/jsDeclarationsTypeReferences2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ export declare const o: {
3838
m: number;
3939
};
4040
//// [index.d.ts]
41-
export const thing: any;
41+
export const thing: number;

tests/baselines/reference/jsDeclarationsTypeReferences2.types

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
=== tests/cases/conformance/jsdoc/declarations/index.js ===
22
const{ a, m } = require("./something").o;
3-
>a : any
4-
>m : any
3+
>a : number
4+
>m : number
55
>require("./something").o : { a: number; m: number; }
66
>require("./something") : typeof import("tests/cases/conformance/jsdoc/declarations/something")
77
>require : any
88
>"./something" : "./something"
99
>o : { a: number; m: number; }
1010

1111
const thing = a + m
12-
>thing : any
13-
>a + m : any
14-
>a : any
15-
>m : any
12+
>thing : number
13+
>a + m : number
14+
>a : number
15+
>m : number
1616

1717
module.exports = {
18-
>module.exports = { thing} : { thing: any; }
19-
>module.exports : { thing: any; }
20-
>module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": { thing: any; }; }
21-
>exports : { thing: any; }
22-
>{ thing} : { thing: any; }
18+
>module.exports = { thing} : { thing: number; }
19+
>module.exports : { thing: number; }
20+
>module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": { thing: number; }; }
21+
>exports : { thing: number; }
22+
>{ thing} : { thing: number; }
2323

2424
thing
25-
>thing : any
25+
>thing : number
2626

2727
};
2828

0 commit comments

Comments
 (0)