From f2896e0596f30b6e7e8ca6d275e918eb8fa708b4 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 31 Oct 2024 12:08:22 -0700 Subject: [PATCH] fix: Preserve type members of namespace in re-exported module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we would generate `declare const …: typeof …` which declares an object with the same value members but none of the type members. Fixes #322. Signed-off-by: Anders Kaseorg --- src/transform/NamespaceFixer.ts | 3 +++ src/transform/Transformer.ts | 6 ++++++ tests/testcases/re-export-namespace-inner/expected.d.ts | 9 +++++++++ tests/testcases/re-export-namespace-inner/index.d.ts | 2 ++ tests/testcases/re-export-namespace-inner/mod.d.ts | 5 +++++ 5 files changed, 25 insertions(+) create mode 100644 tests/testcases/re-export-namespace-inner/expected.d.ts create mode 100644 tests/testcases/re-export-namespace-inner/index.d.ts create mode 100644 tests/testcases/re-export-namespace-inner/mod.d.ts diff --git a/src/transform/NamespaceFixer.ts b/src/transform/NamespaceFixer.ts index 5cd8abe..fb58e07 100644 --- a/src/transform/NamespaceFixer.ts +++ b/src/transform/NamespaceFixer.ts @@ -197,6 +197,9 @@ export class NamespaceFixer { const typeParams = renderTypeParams(generics); code += `type ${ns.name}_${exportedName}${typeParams.in} = ${localName}${typeParams.out};\n`; code += `declare const ${ns.name}_${exportedName}: typeof ${localName};\n`; + } else if (type === "namespace") { + // namespaces may contain both types and values + code += `import ${ns.name}_${exportedName} = ${localName};\n`; } else { // functions and vars are just values code += `declare const ${ns.name}_${exportedName}: typeof ${localName};\n`; diff --git a/src/transform/Transformer.ts b/src/transform/Transformer.ts index c6021b4..f796a1f 100644 --- a/src/transform/Transformer.ts +++ b/src/transform/Transformer.ts @@ -250,6 +250,12 @@ class Transformer { convertImportDeclaration(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration) { if (ts.isImportEqualsDeclaration(node)) { + if (ts.isEntityName(node.moduleReference)) { + const scope = this.createDeclaration(node, node.name); + scope.pushReference(scope.convertEntityName(node.moduleReference)); + return; + } + // assume its like `import default` if (!ts.isExternalModuleReference(node.moduleReference)) { throw new UnsupportedSyntaxError(node, "ImportEquals should have a literal source."); diff --git a/tests/testcases/re-export-namespace-inner/expected.d.ts b/tests/testcases/re-export-namespace-inner/expected.d.ts new file mode 100644 index 0000000..b319b42 --- /dev/null +++ b/tests/testcases/re-export-namespace-inner/expected.d.ts @@ -0,0 +1,9 @@ +declare namespace inner { + type Ty = number; + const num: number; +} +import mod_d_inner = inner; +declare namespace mod_d { + export { mod_d_inner as inner }; +} +export { mod_d as outer }; diff --git a/tests/testcases/re-export-namespace-inner/index.d.ts b/tests/testcases/re-export-namespace-inner/index.d.ts new file mode 100644 index 0000000..c859b6c --- /dev/null +++ b/tests/testcases/re-export-namespace-inner/index.d.ts @@ -0,0 +1,2 @@ +import * as outer from "./mod"; +export { outer }; diff --git a/tests/testcases/re-export-namespace-inner/mod.d.ts b/tests/testcases/re-export-namespace-inner/mod.d.ts new file mode 100644 index 0000000..6119a54 --- /dev/null +++ b/tests/testcases/re-export-namespace-inner/mod.d.ts @@ -0,0 +1,5 @@ +declare namespace inner { + type Ty = number; + const num: number; +} +export { inner };