mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-02 14:48:32 -05:00
Use control flow to type CommonJS exports (#42751)
* Allow redeclaring CommonJS alias with initial undefined This allows us to read our own output, plus the times when people manually write exactly the same pattern. Fixes #40555 * Use control flow to type commonjs exports 1. Could probably use a *lot* more tests. 2. getTypeOfAlias redoes some work from resolveAlias because it needs to not resolve the alias completely, just to its export. * fix lint, improve jsdoc * Add tests, improve+fix control flow 1. Update the module.exports test to match the exports ones. 2. Add a test of evolving commonjs type. 3. Add a test of assignment as last statement. (1) exposed a bug that required a better synthetic reference. (3) exposed a bug that was most easily fixed by giving source files a `endFlowNode` like functions and setting it in the binder. * fix lint
This commit is contained in:
committed by
GitHub
parent
eebb89533b
commit
dd1ef88d01
@@ -683,6 +683,7 @@ namespace ts {
|
||||
}
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
node.flags |= emitFlags;
|
||||
(node as SourceFile).endFlowNode = currentFlow;
|
||||
}
|
||||
|
||||
if (currentReturnTarget) {
|
||||
|
||||
@@ -2473,7 +2473,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined {
|
||||
return symbol.declarations?.find(isAliasSymbolDeclaration);
|
||||
return symbol.declarations && findLast<Declaration>(symbol.declarations, isAliasSymbolDeclaration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8449,6 +8449,23 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
/** Create a synthetic property access flow node after the last statement of the file */
|
||||
function getFlowTypeFromCommonJSExport(symbol: Symbol) {
|
||||
const file = getSourceFileOfNode(symbol.declarations![0]);
|
||||
const accessName = unescapeLeadingUnderscores(symbol.escapedName);
|
||||
const areAllModuleExports = symbol.declarations!.every(d => isInJSFile(d) && isAccessExpression(d) && isModuleExportsAccessExpression(d.expression));
|
||||
const reference = areAllModuleExports
|
||||
? factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier("module"), factory.createIdentifier("exports")), accessName)
|
||||
: factory.createPropertyAccessExpression(factory.createIdentifier("exports"), accessName);
|
||||
if (areAllModuleExports) {
|
||||
setParent((reference.expression as PropertyAccessExpression).expression, reference.expression);
|
||||
}
|
||||
setParent(reference.expression, reference);
|
||||
setParent(reference, file);
|
||||
reference.flowNode = file.endFlowNode;
|
||||
return getFlowTypeOfReference(reference, autoType, undefinedType);
|
||||
}
|
||||
|
||||
function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) {
|
||||
const accessName = startsWith(symbol.escapedName as string, "__#")
|
||||
? factory.createPrivateIdentifier((symbol.escapedName as string).split("@")[1])
|
||||
@@ -9186,14 +9203,15 @@ namespace ts {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
const targetSymbol = resolveAlias(symbol);
|
||||
|
||||
const exportSymbol = symbol.declarations && getTargetOfAliasDeclaration(getDeclarationOfAliasSymbol(symbol)!, /*dontResolveAlias*/ true);
|
||||
// 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.
|
||||
// This check is important because without it, a call to getTypeOfSymbol could end
|
||||
// up recursively calling getTypeOfAlias, causing a stack overflow.
|
||||
links.type = targetSymbol.flags & SymbolFlags.Value
|
||||
? getTypeOfSymbol(targetSymbol)
|
||||
links.type = exportSymbol?.declarations && isDuplicatedCommonJSExport(exportSymbol.declarations) && symbol.declarations!.length ? getFlowTypeFromCommonJSExport(exportSymbol)
|
||||
: isDuplicatedCommonJSExport(symbol.declarations) ? autoType
|
||||
: targetSymbol.flags & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol)
|
||||
: errorType;
|
||||
}
|
||||
return links.type;
|
||||
@@ -27133,7 +27151,10 @@ namespace ts {
|
||||
// accessor, or optional method.
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
if (assignmentKind === AssignmentKind.Definite ||
|
||||
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
|
||||
prop &&
|
||||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor))
|
||||
&& !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)
|
||||
&& !isDuplicatedCommonJSExport(prop.declarations)) {
|
||||
return propType;
|
||||
}
|
||||
if (propType === autoType) {
|
||||
@@ -38154,9 +38175,11 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
if (exportedDeclarationsCount > 1) {
|
||||
for (const declaration of declarations!) {
|
||||
if (isNotOverload(declaration)) {
|
||||
diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id)));
|
||||
if (!isDuplicatedCommonJSExport(declarations)) {
|
||||
for (const declaration of declarations!) {
|
||||
if (isNotOverload(declaration)) {
|
||||
diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38166,6 +38189,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isDuplicatedCommonJSExport(declarations: Declaration[] | undefined) {
|
||||
return declarations
|
||||
&& declarations.length > 1
|
||||
&& declarations.every(d => isInJSFile(d) && isAccessExpression(d) && (isExportsIdentifier(d.expression) || isModuleExportsAccessExpression(d.expression)));
|
||||
}
|
||||
|
||||
function checkSourceElement(node: Node | undefined): void {
|
||||
if (node) {
|
||||
const saveCurrentNode = currentNode;
|
||||
|
||||
@@ -3545,6 +3545,7 @@ namespace ts {
|
||||
/* @internal */ localJsxFragmentFactory?: EntityName;
|
||||
|
||||
/* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
|
||||
/* @internal */ endFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
Reference in New Issue
Block a user