module.exports = Entity is an alias, just like export = Entity (#23570)

* Make `module.export =` an alias like `export=` is

This breaks a couple of tests for previous workarounds. Fix in upcoming
commits.

* Basically fixes all the breaks, but needs cleanup

* More notes to myself

* Clean up TODOs

* Call mergeSymbolTable and delete export= afterward

instead of basically copying the code myself.

* More cleanup

* Remove unnecessary check in import type checking

* Revert to DIY code.

It is more correct and will go away in a few days.

* Exported class expressions can be used as type

In both JS and TS

* Do not require named class expressions
This commit is contained in:
Nathan Shively-Sanders
2018-04-23 15:24:31 -07:00
committed by GitHub
parent ef8af93329
commit 905f9a02ad
18 changed files with 402 additions and 27 deletions
+6 -3
View File
@@ -2220,14 +2220,14 @@ namespace ts {
bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String);
}
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
function bindExportAssignment(node: ExportAssignment) {
if (!container.symbol || !container.symbol.exports) {
// Export assignment in some sort of block construct
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node));
}
else {
const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node)
// An export default clause with an EntityNameExpression exports all meanings of that identifier
// An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression;
? SymbolFlags.Alias
// An export default clause with any other expression exports a value
: SymbolFlags.Property;
@@ -2322,7 +2322,10 @@ namespace ts {
// 'module.exports = expr' assignment
setCommonJsModuleIndicator(node);
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule, SymbolFlags.None);
const flags = exportAssignmentIsAlias(node)
? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class
: SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule;
declareSymbol(file.symbol.exports, file.symbol, node, flags, SymbolFlags.None);
}
function bindThisPropertyAssignment(node: BinaryExpression | PropertyAccessExpression) {
+22 -9
View File
@@ -1926,13 +1926,17 @@ namespace ts {
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol | undefined {
const aliasLike = resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontResolveAlias);
function getTargetOfExportAssignment(node: ExportAssignment | BinaryExpression, dontResolveAlias: boolean): Symbol | undefined {
const expression = (isExportAssignment(node) ? node.expression : node.right) as EntityNameExpression | ClassExpression;
if (isClassExpression(expression)) {
return checkExpression(expression).symbol;
}
const aliasLike = resolveEntityName(expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontResolveAlias);
if (aliasLike) {
return aliasLike;
}
checkExpression(node.expression);
return getNodeLinks(node.expression).resolvedSymbol;
checkExpression(expression);
return getNodeLinks(expression).resolvedSymbol;
}
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol | undefined {
@@ -1948,7 +1952,8 @@ namespace ts {
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(<ExportSpecifier>node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
case SyntaxKind.BinaryExpression:
return getTargetOfExportAssignment((<ExportAssignment | BinaryExpression>node), dontRecursivelyResolve);
case SyntaxKind.NamespaceExportDeclaration:
return getTargetOfNamespaceExportDeclaration(<NamespaceExportDeclaration>node, dontRecursivelyResolve);
}
@@ -2229,20 +2234,28 @@ namespace ts {
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(getCommonJsExportEquals(moduleSymbol), dontResolveAlias)) || moduleSymbol;
return moduleSymbol && getMergedSymbol(getCommonJsExportEquals(resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias), moduleSymbol)) || moduleSymbol;
}
function getCommonJsExportEquals(moduleSymbol: Symbol): Symbol {
const exported = moduleSymbol.exports.get(InternalSymbolName.ExportEquals);
if (!exported || !exported.exports || moduleSymbol.exports.size === 1) {
function getCommonJsExportEquals(exported: Symbol, moduleSymbol: Symbol): Symbol {
if (!exported || moduleSymbol.exports.size === 1) {
return exported;
}
const merged = cloneSymbol(exported);
if (merged.exports === undefined) {
merged.flags = merged.flags | SymbolFlags.ValueModule;
merged.exports = createSymbolTable();
}
moduleSymbol.exports.forEach((s, name) => {
if (name === InternalSymbolName.ExportEquals) return;
if (!merged.exports.has(name)) {
merged.exports.set(name, s);
}
else {
const ms = cloneSymbol(merged.exports.get(name));
mergeSymbol(ms, s);
merged.exports.set(name, ms);
}
});
return merged;
}
+5 -3
View File
@@ -2140,11 +2140,13 @@ namespace ts {
node.kind === SyntaxKind.NamespaceImport ||
node.kind === SyntaxKind.ImportSpecifier ||
node.kind === SyntaxKind.ExportSpecifier ||
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node);
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node) ||
isBinaryExpression(node) && getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports;
}
export function exportAssignmentIsAlias(node: ExportAssignment): boolean {
return isEntityNameExpression(node.expression);
export function exportAssignmentIsAlias(node: ExportAssignment | BinaryExpression): boolean {
const e = isExportAssignment(node) ? node.expression : node.right;
return isEntityNameExpression(e) || isClassExpression(e);
}
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) {