From 7909d8769d2fa3c355333ba831a5cafb6b9c0fce Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 14 Jun 2023 18:06:03 -0700 Subject: [PATCH 1/2] unify notions of implicitly exported between checker and binder --- src/compiler/binder.ts | 6 +++-- src/compiler/checker.ts | 4 +++- src/compiler/utilities.ts | 7 ++++++ ...MergedWithFunctionDefaultExport.errors.txt | 19 +++++++++++++++ ...NotMergedWithFunctionDefaultExport.symbols | 24 +++++++++++++++++++ ...ceNotMergedWithFunctionDefaultExport.types | 23 ++++++++++++++++++ ...spaceNotMergedWithFunctionDefaultExport.ts | 14 +++++++++++ 7 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.errors.txt create mode 100644 tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.symbols create mode 100644 tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.types create mode 100644 tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f54a3e0666f00..6f016a2498e9f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -455,7 +455,8 @@ function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visite return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value } -const enum ContainerFlags { +/** @internal */ +export const enum ContainerFlags { // The current node is not a container, and no container manipulation should happen before // recursing into it. None = 0, @@ -3768,7 +3769,8 @@ export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Ex return false; } -function getContainerFlags(node: Node): ContainerFlags { +/** @internal */ +export function getContainerFlags(node: Node): ContainerFlags { switch (node.kind) { case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bdf2dff585a47..19cc02df1a8b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -274,6 +274,7 @@ import { getEmitModuleResolutionKind, getEmitScriptTarget, getEnclosingBlockScopeContainer, + getEnclosingContainer, getEntityNameFromTypeNode, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, @@ -39024,7 +39025,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { n.parent.kind !== SyntaxKind.ClassDeclaration && n.parent.kind !== SyntaxKind.ClassExpression && n.flags & NodeFlags.Ambient) { - if (!(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) { + const container = getEnclosingContainer(n); + if ((container.flags & NodeFlags.ExportContext) && !(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) { // It is nested in an ambient context, which means it is automatically exported flags |= ModifierFlags.Export; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 54b8f92786698..4145444ae6f2e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -73,6 +73,7 @@ import { ConditionalExpression, ConstructorDeclaration, ConstructSignatureDeclaration, + ContainerFlags, contains, containsPath, createGetCanonicalFileName, @@ -162,6 +163,7 @@ import { GetCanonicalFileName, getCombinedModifierFlags, getCombinedNodeFlags, + getContainerFlags, getDirectoryPath, getJSDocAugmentsTag, getJSDocDeprecatedTagNoCache, @@ -2022,6 +2024,11 @@ export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport { return isAnyImportSyntax(node) || isExportDeclaration(node); } +/** @internal */ +export function getEnclosingContainer(node: Node): Node { + return findAncestor(node.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer))!; +} + // Gets the nearest enclosing block scope container that has the provided node // as a descendant, that is not the provided node. /** @internal */ diff --git a/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.errors.txt b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.errors.txt new file mode 100644 index 0000000000000..9874a41d1a3ce --- /dev/null +++ b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.errors.txt @@ -0,0 +1,19 @@ +replace-in-file/types/index.d.ts(4,19): error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local. +replace-in-file/types/index.d.ts(7,13): error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local. + + +==== replace-in-file/types/index.d.ts (2 errors) ==== + // repro from https://github.com/microsoft/TypeScript/issues/54342 + + declare module 'replace-in-file' { + export function replaceInFile(config: unknown): Promise; + ~~~~~~~~~~~~~ +!!! error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local. + export default replaceInFile; + + namespace replaceInFile { + ~~~~~~~~~~~~~ +!!! error TS2395: Individual declarations in merged declaration 'replaceInFile' must be all exported or all local. + export function sync(config: unknown): unknown[]; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.symbols b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.symbols new file mode 100644 index 0000000000000..69923fe4dcd63 --- /dev/null +++ b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.symbols @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts] //// + +=== replace-in-file/types/index.d.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54342 + +declare module 'replace-in-file' { +>'replace-in-file' : Symbol("replace-in-file", Decl(index.d.ts, 0, 0)) + + export function replaceInFile(config: unknown): Promise; +>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34)) +>config : Symbol(config, Decl(index.d.ts, 3, 32)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + + export default replaceInFile; +>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34), Decl(index.d.ts, 4, 31)) + + namespace replaceInFile { +>replaceInFile : Symbol(replaceInFile, Decl(index.d.ts, 2, 34), Decl(index.d.ts, 4, 31)) + + export function sync(config: unknown): unknown[]; +>sync : Symbol(sync, Decl(index.d.ts, 6, 27)) +>config : Symbol(config, Decl(index.d.ts, 7, 25)) + } +} diff --git a/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.types b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.types new file mode 100644 index 0000000000000..d7101b25dde5d --- /dev/null +++ b/tests/baselines/reference/namespaceNotMergedWithFunctionDefaultExport.types @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts] //// + +=== replace-in-file/types/index.d.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54342 + +declare module 'replace-in-file' { +>'replace-in-file' : typeof import("replace-in-file") + + export function replaceInFile(config: unknown): Promise; +>replaceInFile : (config: unknown) => Promise +>config : unknown + + export default replaceInFile; +>replaceInFile : typeof replaceInFile + + namespace replaceInFile { +>replaceInFile : typeof replaceInFile + + export function sync(config: unknown): unknown[]; +>sync : (config: unknown) => unknown[] +>config : unknown + } +} diff --git a/tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts b/tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts new file mode 100644 index 0000000000000..ea86d2ca89ccd --- /dev/null +++ b/tests/cases/compiler/namespaceNotMergedWithFunctionDefaultExport.ts @@ -0,0 +1,14 @@ +// @moduleResolution: node10 +// @module: commonjs + +// repro from https://github.com/microsoft/TypeScript/issues/54342 + +// @Filename: replace-in-file/types/index.d.ts +declare module 'replace-in-file' { + export function replaceInFile(config: unknown): Promise; + export default replaceInFile; + + namespace replaceInFile { + export function sync(config: unknown): unknown[]; + } +} \ No newline at end of file From 777e7beb0f0c8ebfb1c39ed170763f303b44d7ea Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 15 Jun 2023 12:56:49 -0700 Subject: [PATCH 2/2] refactor --- src/compiler/binder.ts | 3 ++- src/compiler/checker.ts | 4 ++-- src/compiler/utilities.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6f016a2498e9f..4ebea0c522de0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -87,6 +87,7 @@ import { getElementOrPropertyAccessName, getEmitScriptTarget, getEnclosingBlockScopeContainer, + getEnclosingContainer, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getEscapedTextOfJsxNamespacedName, @@ -2357,7 +2358,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { const saveCurrentFlow = currentFlow; for (const typeAlias of delayedTypeAliases) { const host = typeAlias.parent.parent; - container = (findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) as IsContainer | undefined) || file; + container = (getEnclosingContainer(host) as IsContainer | undefined) || file; blockScopeContainer = (getEnclosingBlockScopeContainer(host) as IsBlockScopedContainer | undefined) || file; currentFlow = initFlowNode({ flags: FlowFlags.Start }); parent = typeAlias; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 19cc02df1a8b7..47a06c273be19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39026,8 +39026,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { n.parent.kind !== SyntaxKind.ClassExpression && n.flags & NodeFlags.Ambient) { const container = getEnclosingContainer(n); - if ((container.flags & NodeFlags.ExportContext) && !(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) { - // It is nested in an ambient context, which means it is automatically exported + if ((container && container.flags & NodeFlags.ExportContext) && !(flags & ModifierFlags.Ambient) && !(isModuleBlock(n.parent) && isModuleDeclaration(n.parent.parent) && isGlobalScopeAugmentation(n.parent.parent))) { + // It is nested in an ambient export context, which means it is automatically exported flags |= ModifierFlags.Export; } flags |= ModifierFlags.Ambient; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4145444ae6f2e..c681495eda0cb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2025,8 +2025,8 @@ export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport { } /** @internal */ -export function getEnclosingContainer(node: Node): Node { - return findAncestor(node.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer))!; +export function getEnclosingContainer(node: Node): Node | undefined { + return findAncestor(node.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)); } // Gets the nearest enclosing block scope container that has the provided node