Skip to content

Commit 49deff2

Browse files
authored
Make the implicitly exported rule consistent between checker and binder (#54659)
1 parent 70fe93f commit 49deff2

7 files changed

+97
-5
lines changed

src/compiler/binder.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import {
8787
getElementOrPropertyAccessName,
8888
getEmitScriptTarget,
8989
getEnclosingBlockScopeContainer,
90+
getEnclosingContainer,
9091
getErrorSpanForNode,
9192
getEscapedTextOfIdentifierOrLiteral,
9293
getEscapedTextOfJsxNamespacedName,
@@ -455,7 +456,8 @@ function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visite
455456
return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value
456457
}
457458

458-
const enum ContainerFlags {
459+
/** @internal */
460+
export const enum ContainerFlags {
459461
// The current node is not a container, and no container manipulation should happen before
460462
// recursing into it.
461463
None = 0,
@@ -2356,7 +2358,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
23562358
const saveCurrentFlow = currentFlow;
23572359
for (const typeAlias of delayedTypeAliases) {
23582360
const host = typeAlias.parent.parent;
2359-
container = (findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) as IsContainer | undefined) || file;
2361+
container = (getEnclosingContainer(host) as IsContainer | undefined) || file;
23602362
blockScopeContainer = (getEnclosingBlockScopeContainer(host) as IsBlockScopedContainer | undefined) || file;
23612363
currentFlow = initFlowNode({ flags: FlowFlags.Start });
23622364
parent = typeAlias;
@@ -3768,7 +3770,8 @@ export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Ex
37683770
return false;
37693771
}
37703772

3771-
function getContainerFlags(node: Node): ContainerFlags {
3773+
/** @internal */
3774+
export function getContainerFlags(node: Node): ContainerFlags {
37723775
switch (node.kind) {
37733776
case SyntaxKind.ClassExpression:
37743777
case SyntaxKind.ClassDeclaration:

src/compiler/checker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ import {
274274
getEmitModuleResolutionKind,
275275
getEmitScriptTarget,
276276
getEnclosingBlockScopeContainer,
277+
getEnclosingContainer,
277278
getEntityNameFromTypeNode,
278279
getErrorSpanForNode,
279280
getEscapedTextOfIdentifierOrLiteral,
@@ -39038,8 +39039,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3903839039
n.parent.kind !== SyntaxKind.ClassDeclaration &&
3903939040
n.parent.kind !== SyntaxKind.ClassExpression &&
3904039041
n.flags & NodeFlags.Ambient) {
39041-
if (!(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) {
39042-
// It is nested in an ambient context, which means it is automatically exported
39042+
const container = getEnclosingContainer(n);
39043+
if ((container && container.flags & NodeFlags.ExportContext) && !(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) {
39044+
// It is nested in an ambient export context, which means it is automatically exported
3904339045
flags |= ModifierFlags.Export;
3904439046
}
3904539047
flags |= ModifierFlags.Ambient;

src/compiler/utilities.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import {
7373
ConditionalExpression,
7474
ConstructorDeclaration,
7575
ConstructSignatureDeclaration,
76+
ContainerFlags,
7677
contains,
7778
containsPath,
7879
createGetCanonicalFileName,
@@ -162,6 +163,7 @@ import {
162163
GetCanonicalFileName,
163164
getCombinedModifierFlags,
164165
getCombinedNodeFlags,
166+
getContainerFlags,
165167
getDirectoryPath,
166168
getJSDocAugmentsTag,
167169
getJSDocDeprecatedTagNoCache,
@@ -2022,6 +2024,11 @@ export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport {
20222024
return isAnyImportSyntax(node) || isExportDeclaration(node);
20232025
}
20242026

2027+
/** @internal */
2028+
export function getEnclosingContainer(node: Node): Node | undefined {
2029+
return findAncestor(node.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer));
2030+
}
2031+
20252032
// Gets the nearest enclosing block scope container that has the provided node
20262033
// as a descendant, that is not the provided node.
20272034
/** @internal */
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
replace-in-file/types/index.d.ts(4,19): error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local.
2+
replace-in-file/types/index.d.ts(7,13): error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local.
3+
4+
5+
==== replace-in-file/types/index.d.ts (2 errors) ====
6+
// repro from https://github.com/microsoft/TypeScript/issues/54342
7+
8+
declare module 'replace-in-file' {
9+
export function replaceInFile(config: unknown): Promise<unknown[]>;
10+
~~~~~~~~~~~~~
11+
!!! error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local.
12+
export default replaceInFile;
13+
14+
namespace replaceInFile {
15+
~~~~~~~~~~~~~
16+
!!! error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local.
17+
export function sync(config: unknown): unknown[];
18+
}
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts] ////
2+
3+
=== replace-in-file/types/index.d.ts ===
4+
// repro from https://github.com/microsoft/TypeScript/issues/54342
5+
6+
declare module 'replace-in-file' {
7+
>'replace-in-file' : Symbol("replace-in-file", Decl(index.d.ts, 0, 0))
8+
9+
export function replaceInFile(config: unknown): Promise<unknown[]>;
10+
>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34))
11+
>config : Symbol(config, Decl(index.d.ts, 3, 32))
12+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
13+
14+
export default replaceInFile;
15+
>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34), Decl(index.d.ts, 4, 31))
16+
17+
namespace replaceInFile {
18+
>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34), Decl(index.d.ts, 4, 31))
19+
20+
export function sync(config: unknown): unknown[];
21+
>sync : Symbol(sync, Decl(index.d.ts, 6, 27))
22+
>config : Symbol(config, Decl(index.d.ts, 7, 25))
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts] ////
2+
3+
=== replace-in-file/types/index.d.ts ===
4+
// repro from https://github.com/microsoft/TypeScript/issues/54342
5+
6+
declare module 'replace-in-file' {
7+
>'replace-in-file' : typeof import("replace-in-file")
8+
9+
export function replaceInFile(config: unknown): Promise<unknown[]>;
10+
>replaceInFile : (config: unknown) => Promise<unknown[]>
11+
>config : unknown
12+
13+
export default replaceInFile;
14+
>replaceInFile : typeof replaceInFile
15+
16+
namespace replaceInFile {
17+
>replaceInFile : typeof replaceInFile
18+
19+
export function sync(config: unknown): unknown[];
20+
>sync : (config: unknown) => unknown[]
21+
>config : unknown
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @moduleResolution: node10
2+
// @module: commonjs
3+
4+
// repro from https://github.com/microsoft/TypeScript/issues/54342
5+
6+
// @Filename: replace-in-file/types/index.d.ts
7+
declare module 'replace-in-file' {
8+
export function replaceInFile(config: unknown): Promise<unknown[]>;
9+
export default replaceInFile;
10+
11+
namespace replaceInFile {
12+
export function sync(config: unknown): unknown[];
13+
}
14+
}

0 commit comments

Comments
 (0)