Repository: wasm-bindgen/wasm-bindgen (2026-03-24T23:06:43Z)

Issue:
`skip_typescript` is ignored when using `reexport`
Given:

```
#[wasm_bindgen::prelude::wasm_bindgen(module = "my-codes")]
extern "C" {
    #[wasm_bindgen(reexport, js_name = "LanguageCode", skip_typescript)]
    fn __helper_language_code();
}
```

I would expect it to not generate any output in the `.d.ts`, but it actually does generate this entry:

```
export let LanguageCode: unknown;
```

Removing `reexport` causes the typescript to disappear


Discussion:
Thanks for reporting.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d87659727e7..fc930970768 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -95,6 +95,9 @@
   `js_sys_unstable_apis`.
   [#5052](https://github.com/wasm-bindgen/wasm-bindgen/pull/5052)
 
+* Fixed `skip_typescript` not being respected when using `reexport`, causing
+  TypeScript definitions to be incorrectly emitted for re-exported items marked
+  with `#[wasm_bindgen(skip_typescript)]`.
 ### Removed
 
 ## [0.2.114](https://github.com/wasm-bindgen/wasm-bindgen/compare/0.2.113...0.2.114)
diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index b34ebc78a53..60ebe45ab21 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -418,9 +418,13 @@ impl<'a> Context<'a> {
         };
 
         // Process reexports
-        for (export_name, js_import) in self.aux.reexports.clone() {
+        for (export_name, (js_import, generate_typescript)) in self.aux.reexports.clone() {
             let import_name = self.import_name(&js_import)?;
-            let ts_definition = format!("let {import_name}: unknown;\n");
+            let ts_definition = if generate_typescript {
+                format!("let {import_name}: unknown;\n")
+            } else {
+                String::new()
+            };
             define_export(
                 &mut self.exports,
                 &export_name,
@@ -3676,7 +3680,7 @@ if (require('worker_threads').isMainThread) {{
         }
 
         // Ensure all imports for reexports are defined
-        for js_import in self.aux.reexports.values() {
+        for (js_import, _) in self.aux.reexports.values() {
             self.import_name(js_import)?;
         }
 
diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs
index 947b4dfc784..f923bbdfe0d 100644
--- a/crates/cli-support/src/wit/mod.rs
+++ b/crates/cli-support/src/wit/mod.rs
@@ -727,13 +727,21 @@ impl<'a> Context<'a> {
             function,
             assert_no_shim,
         } = function;
+        let generate_typescript = import.generate_typescript;
         let (import_id, _id) = match self.function_imports.get(shim) {
             Some(pair) => *pair,
             None => {
                 if let Some(reexport_name) = import.reexport {
                     self.aux.reexports.insert(
                         reexport_name,
-                        self.determine_import(&import.module, &import.js_namespace, function.name)?,
+                        (
+                            self.determine_import(
+                                &import.module,
+                                &import.js_namespace,
+                                function.name,
+                            )?,
+                            generate_typescript,
+                        ),
                     );
                 }
                 return Ok(());
@@ -800,7 +808,9 @@ impl<'a> Context<'a> {
                         )?;
 
                         if let Some(reexport_name) = import.reexport {
-                            self.aux.reexports.insert(reexport_name, js_import.clone());
+                            self.aux
+                                .reexports
+                                .insert(reexport_name, (js_import.clone(), generate_typescript));
                         }
 
                         AuxImport::Value(AuxValue::Bare(js_import))
@@ -951,13 +961,21 @@ impl<'a> Context<'a> {
         let decode::ImportKind::Static(static_) = import.kind else {
             unreachable!();
         };
+        let generate_typescript = import.generate_typescript;
         let (import_id, _id) = match self.function_imports.get(static_.shim) {
             Some(pair) => *pair,
             None => {
                 if let Some(reexport_name) = import.reexport {
                     self.aux.reexports.insert(
                         reexport_name,
-                        self.determine_import(&import.module, &import.js_namespace, static_.name)?,
+                        (
+                            self.determine_import(
+                                &import.module,
+                                &import.js_namespace,
+                                static_.name,
+                            )?,
+                            generate_typescript,
+                        ),
                     );
                 }
                 return Ok(());
@@ -987,7 +1005,9 @@ impl<'a> Context<'a> {
         let js = self.determine_import(&import.module, &import.js_namespace, static_.name)?;
 
         if let Some(reexport_name) = import.reexport {
-            self.aux.reexports.insert(reexport_name, js.clone());
+            self.aux
+                .reexports
+                .insert(reexport_name, (js.clone(), generate_typescript));
         }
 
         self.aux
@@ -1027,6 +1047,7 @@ impl<'a> Context<'a> {
             unreachable!();
         };
 
+        let generate_typescript = import.generate_typescript;
         let (import_id, _id) = match self.function_imports.get(type_.instanceof_shim) {
             Some(pair) => *pair,
             None => {
@@ -1035,7 +1056,14 @@ impl<'a> Context<'a> {
                 if let Some(reexport_name) = import.reexport {
                     self.aux.reexports.insert(
                         reexport_name,
-                        self.determine_import(&import.module, &import.js_namespace, type_.name)?,
+                        (
+                            self.determine_import(
+                                &import.module,
+                                &import.js_namespace,
+                                type_.name,
+                            )?,
+                            generate_typescript,
+                        ),
                     );
                 }
                 return Ok(());
@@ -1058,7 +1086,9 @@ impl<'a> Context<'a> {
         // imported item.
         let js_import = self.determine_import(&import.module, &import.js_namespace, type_.name)?;
         if let Some(reexport_name) = import.reexport {
-            self.aux.reexports.insert(reexport_name, js_import.clone());
+            self.aux
+                .reexports
+                .insert(reexport_name, (js_import.clone(), generate_typescript));
         }
         self.aux
             .import_map
diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs
index e3cf1269cd6..08d9fddf101 100644
--- a/crates/cli-support/src/wit/nonstandard.rs
+++ b/crates/cli-support/src/wit/nonstandard.rs
@@ -35,7 +35,8 @@ pub struct WasmBindgenAux {
     pub import_map: HashMap<AdapterId, AuxImport>,
 
     /// List of imports that should be re-exported, keyed by reexport name.
-    pub reexports: BTreeMap<String, JsImport>,
+    /// The bool indicates whether TypeScript definitions should be generated for the reexport.
+    pub reexports: BTreeMap<String, (JsImport, bool)>,
 
     /// Small bits of metadata about imports.
     pub imports_with_catch: HashSet<AdapterId>,
diff --git a/crates/macro-support/src/encode.rs b/crates/macro-support/src/encode.rs
index 40b30fbb5a1..63cae8c6a46 100644
--- a/crates/macro-support/src/encode.rs
+++ b/crates/macro-support/src/encode.rs
@@ -292,6 +292,14 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result<Import<
         })
     });
 
+    // Determine whether TypeScript should be generated for this import.
+    // For functions, this is stored on the Function struct; for types and statics,
+    // skip_typescript is not currently supported so we default to true.
+    let generate_typescript = match &i.kind {
+        ast::ImportKind::Function(f) => f.function.generate_typescript,
+        _ => true,
+    };
+
     Ok(Import {
         module: i
             .module
@@ -300,6 +308,7 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result<Import<
             .transpose()?,
         js_namespace: i.js_namespace.clone(),
         reexport,
+        generate_typescript,
         kind: shared_import_kind(&i.kind, intern)?,
     })
 }
diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs
index 792770b3bc7..6bb339f8ee8 100644
--- a/crates/shared/src/lib.rs
+++ b/crates/shared/src/lib.rs
@@ -40,6 +40,7 @@ macro_rules! shared_api {
             module: Option<ImportModule<'a>>,
             js_namespace: Option<Vec<String>>,
             reexport: Option<String>,
+            generate_typescript: bool,
             kind: ImportKind<'a>,
         }
 
diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs
index 8d646d601eb..3d73729bfd8 100644
--- a/crates/shared/src/schema_hash_approval.rs
+++ b/crates/shared/src/schema_hash_approval.rs
@@ -8,7 +8,7 @@
 // If the schema in this library has changed then:
 //  1. Bump the version in `crates/shared/Cargo.toml`
 //  2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version
-const APPROVED_SCHEMA_FILE_HASH: &str = "4279711543728568852";
+const APPROVED_SCHEMA_FILE_HASH: &str = "12719009666036606543";
 
 #[test]
 fn schema_version() {


Test patch:
diff --git a/crates/cli/tests/reference/reexport.bg.js b/crates/cli/tests/reference/reexport.bg.js
index 65f0302c05a..bf4702f663f 100644
--- a/crates/cli/tests/reference/reexport.bg.js
+++ b/crates/cli/tests/reference/reexport.bg.js
@@ -3,6 +3,7 @@ import { original_config } from 'config';
 import { MY_CONSTANT } from 'constants';
 import { default as _default } from 'default-export-lib';
 import { helperFunction } from 'helpers';
+import { hiddenReexport } from 'hidden-lib';
 import { OriginalName } from 'some-library';
 import { CustomType } from 'types-lib';
 import { original } from 'utils';
@@ -27,6 +28,8 @@ export { _default as default }
 
 export { helperFunction }
 
+export { hiddenReexport }
+
 export { invalid_name as 'invalid-name' }
 
 export { original_config as renamedConfig }
diff --git a/crates/cli/tests/reference/reexport.js b/crates/cli/tests/reference/reexport.js
index f248576d623..86f9d9e7e82 100644
--- a/crates/cli/tests/reference/reexport.js
+++ b/crates/cli/tests/reference/reexport.js
@@ -5,5 +5,5 @@ import { __wbg_set_wasm } from "./reference_test_bg.js";
 __wbg_set_wasm(wasm);
 wasm.__wbindgen_start();
 export {
-    CustomType, MY_CONSTANT, RenamedClass, default, helperFunction, "invalid-name", renamedConfig, renamedFunction
+    CustomType, MY_CONSTANT, RenamedClass, default, helperFunction, hiddenReexport, "invalid-name", renamedConfig, renamedFunction
 } from "./reference_test_bg.js";
diff --git a/crates/cli/tests/reference/reexport.rs b/crates/cli/tests/reference/reexport.rs
index d526cefab27..ecc8fd232ae 100644
--- a/crates/cli/tests/reference/reexport.rs
+++ b/crates/cli/tests/reference/reexport.rs
@@ -60,3 +60,9 @@ extern "C" {
     #[wasm_bindgen(js_name = "original_config", reexport = "renamedConfig")]
     pub static CONFIG: JsValue;
 }
+
+#[wasm_bindgen(module = "hidden-lib")]
+extern "C" {
+    #[wasm_bindgen(reexport, skip_typescript)]
+    pub fn hiddenReexport(x: i32) -> i32;
+}