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.
This commit is contained in:
Nathan Shively-Sanders 2020-09-24 14:42:59 -07:00 committed by GitHub
parent e6fdcce2bf
commit eac75f375d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 51 deletions

View file

@ -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.

View file

@ -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
}

View file

@ -38,4 +38,4 @@ export declare const o: {
m: number;
};
//// [index.d.ts]
export const thing: any;
export const thing: number;

View file

@ -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
};