Repository: napi-rs/napi-rs (2025-07-18T14:25:39Z)

Issue:
Iterator in Generator does have helper methods in Node.js 22+
When I implement a `Generator` with `napi9` like this:

```rust
#[napi(iterator)]
pub struct Foo {}

impl Generator for Foo {
  Yield = bool;
  Next = ();
  Return = ();

  fn next(&mut self, _next: Self::Next) -> Option<Self::Yield> {
    return Some(true);
  }
}
```

And then when I use it like this:
```typescript
const foo = new Foo();
const iter = foo[Symbol.iterator]();
```

`iter` does not have the helpers (`take`, `flatMap`, etc) in Iterator.prototype described here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#instance_methods

I am using Node.js 22


Discussion:
This is because the generators generated by napi-rs are iterables, whereas they should be iterators. In the meantime, you can do this:

```js
function* intoIterator(iterable) {
	for (const element of iterable) {
		yield element;
	}
}
```



Patch:
diff --git a/cli/src/utils/typegen.ts b/cli/src/utils/typegen.ts
index 21b3139bee..27e614fd12 100644
--- a/cli/src/utils/typegen.ts
+++ b/cli/src/utils/typegen.ts
@@ -15,6 +15,7 @@ enum TypeDefKind {
   Type = 'type',
   Fn = 'fn',
   Struct = 'struct',
+  Extends = 'extends',
   Impl = 'impl',
 }
 
@@ -23,6 +24,7 @@ interface TypeDefLine {
   name: string
   original_name?: string
   def: string
+  extends?: string
   js_doc?: string
   js_mod?: string
 }
@@ -57,7 +59,8 @@ function prettyPrint(
       break
 
     case TypeDefKind.Struct:
-      s += `${exportDeclare(ambient)} class ${line.name} {\n${line.def}\n}`
+      const extendsDef = line.extends ? ` extends ${line.extends}` : ''
+      s += `${exportDeclare(ambient)} class ${line.name}${extendsDef} {\n${line.def}\n}`
       if (line.original_name && line.original_name !== line.name) {
         s += `\nexport type ${line.original_name} = ${line.name}`
       }
@@ -185,6 +188,11 @@ function preprocessTypeDef(defs: TypeDefLine[]): Map<string, TypeDefLine[]> {
     if (def.kind === TypeDefKind.Struct) {
       group.push(def)
       classDefs.set(def.name, def)
+    } else if (def.kind === TypeDefKind.Extends) {
+      const classDef = classDefs.get(def.name)
+      if (classDef) {
+        classDef.extends = def.def
+      }
     } else if (def.kind === TypeDefKind.Impl) {
       // merge `impl` into class definition
       const classDef = classDefs.get(def.name)
diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs
index c6893f5828..03b9315992 100644
--- a/crates/backend/src/ast.rs
+++ b/crates/backend/src/ast.rs
@@ -87,6 +87,7 @@ pub struct NapiStruct {
   pub register_name: Ident,
   pub kind: NapiStructKind,
   pub has_lifetime: bool,
+  pub is_generator: bool,
 }
 
 #[derive(Debug, Clone)]
diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs
index 5b8abd2313..46971b2eaa 100644
--- a/crates/backend/src/typegen.rs
+++ b/crates/backend/src/typegen.rs
@@ -20,7 +20,7 @@ pub struct TypeDef {
   pub original_name: Option<String>,
   pub def: String,
   pub js_mod: Option<String>,
-  pub js_doc: String,
+  pub js_doc: JSDoc,
 }
 
 thread_local! {
@@ -33,25 +33,6 @@ fn add_alias(name: String, alias: String) {
   });
 }
 
-pub fn js_doc_from_comments(comments: &[String]) -> String {
-  if comments.is_empty() {
-    return "".to_owned();
-  }
-
-  if comments.len() == 1 {
-    return format!("/**{} */\n", comments[0]);
-  }
-
-  format!(
-    "/**\n{} */\n",
-    comments
-      .iter()
-      .map(|c| format!(" *{c}\n"))
-      .collect::<Vec<String>>()
-      .join("")
-  )
-}
-
 fn escape_json(src: &str) -> String {
   use std::fmt::Write;
   let mut escaped = String::with_capacity(src.len());
@@ -80,6 +61,83 @@ fn escape_json(src: &str) -> String {
   escaped
 }
 
+#[derive(Default, Debug)]
+pub struct JSDoc {
+  blocks: Vec<Vec<String>>,
+}
+
+impl JSDoc {
+  pub fn new<I, S>(initial_lines: I) -> JSDoc
+  where
+    I: IntoIterator<Item = S>,
+    S: Into<String>,
+  {
+    let block = Self::cleanup_lines(initial_lines);
+    if block.is_empty() {
+      return Self { blocks: vec![] };
+    }
+
+    Self {
+      blocks: vec![block],
+    }
+  }
+
+  pub fn add_block<I, S>(&mut self, lines: I)
+  where
+    I: IntoIterator<Item = S>,
+    S: Into<String>,
+  {
+    let v: Vec<String> = Self::cleanup_lines(lines);
+
+    if !v.is_empty() {
+      self.blocks.push(v);
+    }
+  }
+
+  fn cleanup_lines<I, S>(lines: I) -> Vec<String>
+  where
+    I: IntoIterator<Item = S>,
+    S: Into<String>,
+  {
+    let raw: Vec<String> = lines.into_iter().map(Into::into).collect();
+
+    if let (Some(first_non_blank), Some(last_non_blank)) = (
+      raw.iter().position(|l| !l.trim().is_empty()),
+      raw.iter().rposition(|l| !l.trim().is_empty()),
+    ) {
+      raw[first_non_blank..=last_non_blank]
+        .iter()
+        .map(|l| l.trim().to_owned())
+        .collect()
+    } else {
+      Vec::new()
+    }
+  }
+}
+
+impl Display for JSDoc {
+  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+    if self.blocks.is_empty() {
+      return Ok(());
+    }
+
+    if self.blocks.len() == 1 && self.blocks[0].len() == 1 {
+      return writeln!(f, "/** {} */", self.blocks[0][0]);
+    }
+
+    writeln!(f, "/**")?;
+    for (i, block) in self.blocks.iter().enumerate() {
+      for line in block {
+        writeln!(f, " * {line}")?;
+      }
+      if i + 1 != self.blocks.len() {
+        writeln!(f, " *")?;
+      }
+    }
+    writeln!(f, " */")
+  }
+}
+
 impl Display for TypeDef {
   fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
     let js_mod = if let Some(js_mod) = &self.js_mod {
@@ -98,7 +156,7 @@ impl Display for TypeDef {
       r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#,
       self.kind,
       self.name,
-      escape_json(&self.js_doc),
+      escape_json(&self.js_doc.to_string()),
       escape_json(&self.def),
       original_name,
       js_mod,
diff --git a/crates/backend/src/typegen/const.rs b/crates/backend/src/typegen/const.rs
index e3933d4e0c..9338cabbb1 100644
--- a/crates/backend/src/typegen/const.rs
+++ b/crates/backend/src/typegen/const.rs
@@ -1,6 +1,10 @@
 use super::{ToTypeDef, TypeDef};
 
-use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst};
+use crate::{
+  ty_to_ts_type,
+  typegen::{add_alias, JSDoc},
+  NapiConst,
+};
 
 impl ToTypeDef for NapiConst {
   fn to_type_def(&self) -> Option<TypeDef> {
@@ -20,7 +24,7 @@ impl ToTypeDef for NapiConst {
         ty_to_ts_type(&self.type_name, false, false, false).0
       ),
       js_mod: self.js_mod.to_owned(),
-      js_doc: js_doc_from_comments(&self.comments),
+      js_doc: JSDoc::new(&self.comments),
     })
   }
 }
diff --git a/crates/backend/src/typegen/enum.rs b/crates/backend/src/typegen/enum.rs
index 8345f2c884..df8e718d5a 100644
--- a/crates/backend/src/typegen/enum.rs
+++ b/crates/backend/src/typegen/enum.rs
@@ -1,5 +1,5 @@
 use super::{add_alias, ToTypeDef, TypeDef};
-use crate::{js_doc_from_comments, NapiEnum, NapiEnumValue};
+use crate::{typegen::JSDoc, NapiEnum, NapiEnumValue};
 
 impl ToTypeDef for NapiEnum {
   fn to_type_def(&self) -> Option<TypeDef> {
@@ -18,7 +18,7 @@ impl ToTypeDef for NapiEnum {
       name: self.js_name.to_owned(),
       original_name: Some(self.name.to_string()),
       def: self.gen_ts_variants(),
-      js_doc: js_doc_from_comments(&self.comments),
+      js_doc: JSDoc::new(&self.comments),
       js_mod: self.js_mod.to_owned(),
     })
   }
@@ -34,7 +34,7 @@ impl NapiEnum {
           NapiEnumValue::Number(num) => format!("{num}"),
           NapiEnumValue::String(string) => format!("'{string}'"),
         };
-        format!("{}{} = {}", js_doc_from_comments(&v.comments), v.name, val)
+        format!("{}{} = {}", JSDoc::new(&v.comments), v.name, val)
       })
       .collect::<Vec<_>>()
       .join(",\n ")
diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs
index 3cd64ef5f8..00c0495768 100644
--- a/crates/backend/src/typegen/fn.rs
+++ b/crates/backend/src/typegen/fn.rs
@@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter};
 use syn::{Member, Pat, PathArguments, PathSegment};
 
 use super::{r#struct::CLASS_STRUCTS, ty_to_ts_type, ToTypeDef, TypeDef};
-use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
+use crate::{typegen::JSDoc, CallbackArg, FnKind, NapiFn};
 
 pub(crate) struct FnArg {
   pub(crate) arg: String,
@@ -100,7 +100,7 @@ impl ToTypeDef for NapiFn {
       original_name: None,
       def,
       js_mod: self.js_mod.to_owned(),
-      js_doc: js_doc_from_comments(&self.comments),
+      js_doc: JSDoc::new(&self.comments),
     })
   }
 }
@@ -329,7 +329,6 @@ impl NapiFn {
         } else {
           "void".to_owned()
         };
-
         if self.is_async {
           format!(": Promise<{ret}>")
         } else {
diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs
index 067899896b..cb37f3b4b5 100644
--- a/crates/backend/src/typegen/struct.rs
+++ b/crates/backend/src/typegen/struct.rs
@@ -1,10 +1,11 @@
 use std::collections::HashMap;
+use std::vec::Vec;
 use std::{cell::RefCell, iter};
 
 use super::{add_alias, ToTypeDef, TypeDef};
+use crate::typegen::JSDoc;
 use crate::{
-  format_js_property_name, js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct,
-  NapiStructField, NapiStructKind,
+  format_js_property_name, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructField, NapiStructKind,
 };
 
 thread_local! {
@@ -20,6 +21,17 @@ impl ToTypeDef for NapiStruct {
     });
     add_alias(self.name.to_string(), self.js_name.to_string());
 
+    let mut js_doc = JSDoc::new(&self.comments);
+    if self.is_generator {
+      let generator_doc =[
+"This type extends JavaScript's `Iterator`, and so has the iterator helper",
+"methods. It may extend the upcoming TypeScript `Iterator` class in the future.",
+"",
+"@see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods",
+"@see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods", ];
+      js_doc.add_block(generator_doc)
+    }
+
     Some(TypeDef {
       kind: String::from(match self.kind {
         NapiStructKind::Transparent(_) => "type",
@@ -32,7 +44,7 @@ impl ToTypeDef for NapiStruct {
       original_name: Some(self.name.to_string()),
       def: self.gen_ts_class(),
       js_mod: self.js_mod.to_owned(),
-      js_doc: js_doc_from_comments(&self.comments),
+      js_doc,
     })
   }
 }
@@ -67,17 +79,17 @@ impl ToTypeDef for NapiImpl {
         "void".to_owned()
       };
       Some(TypeDef {
-        kind: "impl".to_owned(),
+        kind: "extends".to_owned(),
         name: self.js_name.to_owned(),
         original_name: None,
         def: format!(
-          "[Symbol.iterator](): Iterator<{}, {}, {}>",
+          "Iterator<{}, {}, {}>",
           ty_to_ts_type(output_type, false, true, false).0,
           return_type,
           next_type,
         ),
         js_mod: self.js_mod.to_owned(),
-        js_doc: "".to_string(),
+        js_doc: JSDoc::new::<Vec<String>, String>(Vec::default()),
       })
     } else {
       Some(TypeDef {
@@ -93,7 +105,7 @@ impl ToTypeDef for NapiImpl {
             } else {
               Some(format!(
                 "{}{}",
-                js_doc_from_comments(&f.comments),
+                JSDoc::new(&f.comments),
                 f.to_type_def()
                   .map_or(String::default(), |type_def| type_def.def)
               ))
@@ -102,7 +114,7 @@ impl ToTypeDef for NapiImpl {
           .collect::<Vec<_>>()
           .join("\\n"),
         js_mod: self.js_mod.to_owned(),
-        js_doc: "".to_string(),
+        js_doc: JSDoc::new::<Vec<String>, String>(Vec::default()),
       })
     }
   }
@@ -117,7 +129,7 @@ impl NapiStruct {
     let mut field_str = String::from("");
 
     if !f.comments.is_empty() {
-      field_str.push_str(&js_doc_from_comments(&f.comments))
+      field_str.push_str(&format!("{}", JSDoc::new(&f.comments)))
     }
 
     if !f.setter {
diff --git a/crates/backend/src/typegen/type.rs b/crates/backend/src/typegen/type.rs
index 48808f889c..74bc4c1224 100644
--- a/crates/backend/src/typegen/type.rs
+++ b/crates/backend/src/typegen/type.rs
@@ -1,6 +1,10 @@
 use super::{ToTypeDef, TypeDef};
 
-use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiType};
+use crate::{
+  ty_to_ts_type,
+  typegen::{add_alias, JSDoc},
+  NapiType,
+};
 
 impl ToTypeDef for NapiType {
   fn to_type_def(&self) -> Option<TypeDef> {
@@ -16,7 +20,7 @@ impl ToTypeDef for NapiType {
       original_name: Some(self.name.to_string()),
       def: ty_to_ts_type(&self.value, false, false, false).0,
       js_mod: self.js_mod.to_owned(),
-      js_doc: js_doc_from_comments(&self.comments),
+      js_doc: JSDoc::new(&self.comments),
     })
   }
 }
diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs
index 3d60f016e1..908d40c579 100644
--- a/crates/macro/src/parser/mod.rs
+++ b/crates/macro/src/parser/mod.rs
@@ -1270,6 +1270,22 @@ impl ConvertToAST for syn::ItemStruct {
     record_struct(&rust_struct_ident, final_js_name_for_struct.clone(), opts);
     let namespace = opts.namespace().map(|(m, _)| m.to_owned());
     let implement_iterator = opts.iterator().is_some();
+
+    if implement_iterator
+      && self
+        .fields
+        .iter()
+        .filter(|f| matches!(f.vis, Visibility::Public(_)))
+        .filter_map(|f| f.ident.clone())
+        .map(|ident| ident.to_string())
+        .any(|field_name| field_name == "next" || field_name == "throw" || field_name == "return")
+    {
+      bail_span!(
+        self,
+        "Generator structs cannot have public fields named `next`, `throw`, or `return`."
+      );
+    }
+
     let generator_struct = GENERATOR_STRUCT.get_or_init(|| Mutex::new(HashMap::new()));
     let mut generator_struct = generator_struct
       .lock()
@@ -1393,6 +1409,7 @@ impl ConvertToAST for syn::ItemStruct {
         register_name: get_register_ident(format!("{rust_struct_ident}_struct").as_str()),
         comments: extract_doc_comments(&self.attrs),
         has_lifetime: lifetime.is_some(),
+        is_generator: implement_iterator,
       }),
     })
   }
@@ -1556,6 +1573,7 @@ impl ConvertToAST for syn::ItemEnum {
             object_to_js: opts.object_to_js(),
           }),
           has_lifetime: false,
+          is_generator: false,
         }),
       });
     }
diff --git a/crates/napi/src/bindgen_runtime/iterator.rs b/crates/napi/src/bindgen_runtime/iterator.rs
index d28d626540..c2dec99fe0 100644
--- a/crates/napi/src/bindgen_runtime/iterator.rs
+++ b/crates/napi/src/bindgen_runtime/iterator.rs
@@ -48,170 +48,93 @@ pub unsafe fn create_iterator<T: Generator>(
   let mut global = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe { sys::napi_get_global(env, &mut global) },
+    sys::napi_get_global(env, &mut global),
     "Get global object failed",
   );
+
   let mut symbol_object = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object)
-    },
+    sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object),
     "Get global object failed",
   );
+
   let mut iterator_symbol = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_get_named_property(
-        env,
-        symbol_object,
-        c"iterator".as_ptr().cast(),
-        &mut iterator_symbol,
-      )
-    },
+    sys::napi_get_named_property(
+      env,
+      symbol_object,
+      c"iterator".as_ptr().cast(),
+      &mut iterator_symbol,
+    ),
     "Get Symbol.iterator failed",
   );
-  let mut generator_function = ptr::null_mut();
-  check_status_or_throw!(
-    env,
-    unsafe {
-      sys::napi_create_function(
-        env,
-        c"Iterator".as_ptr().cast(),
-        8,
-        Some(symbol_generator::<T>),
-        generator_ptr as *mut c_void,
-        &mut generator_function,
-      )
-    },
-    "Create iterator function failed",
-  );
-  check_status_or_throw!(
-    env,
-    unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) },
-    "Failed to set Symbol.iterator on class instance",
-  );
-}
 
-#[doc(hidden)]
-pub unsafe extern "C" fn symbol_generator<T: Generator>(
-  env: sys::napi_env,
-  info: sys::napi_callback_info,
-) -> sys::napi_value {
-  let mut this = ptr::null_mut();
-  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
-  let mut argc = 0;
-  let mut generator_ptr = ptr::null_mut();
-  check_status_or_throw!(
-    env,
-    unsafe {
-      sys::napi_get_cb_info(
-        env,
-        info,
-        &mut argc,
-        argv.as_mut_ptr(),
-        &mut this,
-        &mut generator_ptr,
-      )
-    },
-    "Get callback info from generator function failed"
-  );
-  let mut generator_object = ptr::null_mut();
-  check_status_or_throw!(
-    env,
-    unsafe { sys::napi_create_object(env, &mut generator_object) },
-    "Create Generator object failed"
-  );
   let mut next_function = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_create_function(
-        env,
-        c"next".as_ptr().cast(),
-        4,
-        Some(generator_next::<T>),
-        generator_ptr,
-        &mut next_function,
-      )
-    },
+    sys::napi_create_function(
+      env,
+      c"next".as_ptr().cast(),
+      4,
+      Some(generator_next::<T>),
+      generator_ptr as *mut c_void,
+      &mut next_function,
+    ),
     "Create next function failed"
   );
+
   let mut return_function = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_create_function(
-        env,
-        c"return".as_ptr().cast(),
-        6,
-        Some(generator_return::<T>),
-        generator_ptr,
-        &mut return_function,
-      )
-    },
-    "Create next function failed"
+    sys::napi_create_function(
+      env,
+      c"return".as_ptr().cast(),
+      6,
+      Some(generator_return::<T>),
+      generator_ptr as *mut c_void,
+      &mut return_function,
+    ),
+    "Create return function failed"
   );
+
   let mut throw_function = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_create_function(
-        env,
-        c"throw".as_ptr().cast(),
-        5,
-        Some(generator_throw::<T>),
-        generator_ptr,
-        &mut throw_function,
-      )
-    },
-    "Create next function failed"
+    sys::napi_create_function(
+      env,
+      c"throw".as_ptr().cast(),
+      5,
+      Some(generator_throw::<T>),
+      generator_ptr as *mut c_void,
+      &mut throw_function,
+    ),
+    "Create throw function failed"
   );
 
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_set_named_property(
-        env,
-        generator_object,
-        c"next".as_ptr().cast(),
-        next_function,
-      )
-    },
+    sys::napi_set_named_property(env, instance, c"next".as_ptr().cast(), next_function,),
     "Set next function on Generator object failed"
   );
 
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_set_named_property(
-        env,
-        generator_object,
-        c"return".as_ptr().cast(),
-        return_function,
-      )
-    },
+    sys::napi_set_named_property(env, instance, c"return".as_ptr().cast(), return_function),
     "Set return function on Generator object failed"
   );
 
   check_status_or_throw!(
     env,
-    unsafe {
-      sys::napi_set_named_property(
-        env,
-        generator_object,
-        c"throw".as_ptr().cast(),
-        throw_function,
-      )
-    },
+    sys::napi_set_named_property(env, instance, c"throw".as_ptr().cast(), throw_function),
     "Set throw function on Generator object failed"
   );
 
   let mut generator_state = ptr::null_mut();
   check_status_or_throw!(
     env,
-    unsafe { sys::napi_get_boolean(env, false, &mut generator_state) },
+    sys::napi_get_boolean(env, false, &mut generator_state),
     "Create generator state failed"
   );
 
@@ -228,11 +151,117 @@ pub unsafe extern "C" fn symbol_generator<T: Generator>(
 
   check_status_or_throw!(
     env,
-    unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) },
+    sys::napi_define_properties(env, instance, 1, properties.as_ptr()),
     "Define properties on Generator object failed"
   );
 
-  generator_object
+  let mut generator_function = ptr::null_mut();
+  check_status_or_throw!(
+    env,
+    sys::napi_create_function(
+      env,
+      c"Iterator".as_ptr().cast(),
+      8,
+      Some(symbol_generator::<T>),
+      generator_ptr as *mut c_void,
+      &mut generator_function,
+    ),
+    "Create iterator function failed",
+  );
+
+  check_status_or_throw!(
+    env,
+    sys::napi_set_property(env, instance, iterator_symbol, generator_function),
+    "Failed to set Symbol.iterator on class instance",
+  );
+
+  let mut iterator_ctor = ptr::null_mut();
+  check_status_or_throw!(
+    env,
+    sys::napi_get_named_property(env, global, c"Iterator".as_ptr().cast(), &mut iterator_ctor,),
+    "Get Global.Iterator failed",
+  );
+
+  let mut iterator_ctor_type = 0;
+  check_status_or_throw!(
+    env,
+    sys::napi_typeof(env, iterator_ctor, &mut iterator_ctor_type),
+    "Get Global.Iterator type failed",
+  );
+
+  if iterator_ctor_type == sys::ValueType::napi_function {
+    let mut iterator_proto = ptr::null_mut();
+    check_status_or_throw!(
+      env,
+      sys::napi_get_named_property(
+        env,
+        iterator_ctor,
+        c"prototype".as_ptr().cast(),
+        &mut iterator_proto,
+      ),
+      "Failed to get Iterator.prototype",
+    );
+
+    let mut object_ctor = ptr::null_mut();
+    check_status_or_throw!(
+      env,
+      sys::napi_get_named_property(env, global, c"Object".as_ptr().cast(), &mut object_ctor),
+      "Failed to get Object constructor"
+    );
+
+    let mut set_prototype_function = ptr::null_mut();
+    check_status_or_throw!(
+      env,
+      sys::napi_get_named_property(
+        env,
+        object_ctor,
+        c"setPrototypeOf".as_ptr().cast(),
+        &mut set_prototype_function,
+      ),
+      "Failed to get Object.setPrototypeOf"
+    );
+
+    let mut argv = [instance, iterator_proto];
+    check_status_or_throw!(
+      env,
+      sys::napi_call_function(
+        env,
+        object_ctor,
+        set_prototype_function,
+        2,
+        argv.as_mut_ptr(),
+        ptr::null_mut(),
+      ),
+      "Failed to set prototype on object"
+    );
+  }
+}
+
+#[doc(hidden)]
+pub unsafe extern "C" fn symbol_generator<T: Generator>(
+  env: sys::napi_env,
+  info: sys::napi_callback_info,
+) -> sys::napi_value {
+  let mut this = ptr::null_mut();
+  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
+  let mut argc = 0;
+  let mut generator_ptr = ptr::null_mut();
+  check_status_or_throw!(
+    env,
+    unsafe {
+      sys::napi_get_cb_info(
+        env,
+        info,
+        &mut argc,
+        argv.as_mut_ptr(),
+        &mut this,
+        &mut generator_ptr,
+      )
+    },
+    "Get callback info from generator function failed"
+  );
+
+  this
 }
 
 extern "C" fn generator_next<T: Generator>(
diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts
index e725b2f13a..6a916a57c7 100644
--- a/examples/napi/index.d.cts
+++ b/examples/napi/index.d.cts
@@ -175,21 +175,39 @@ export declare class Dog {
   constructor(name: string)
 }
 
-export declare class Fib {
-  [Symbol.iterator](): Iterator<number, void, number>
+/**
+ * This type extends JavaScript's `Iterator`, and so has the iterator helper
+ * methods. It may extend the upcoming TypeScript `Iterator` class in the future.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
+ * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
+ */
+export declare class Fib extends Iterator<number, void, number> {
   constructor()
 }
 
-export declare class Fib2 {
-  [Symbol.iterator](): Iterator<number, void, number>
+/**
+ * This type extends JavaScript's `Iterator`, and so has the iterator helper
+ * methods. It may extend the upcoming TypeScript `Iterator` class in the future.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
+ * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
+ */
+export declare class Fib2 extends Iterator<number, void, number> {
   static create(seed: number): Fib2
 }
 
-export declare class Fib3 {
+/**
+ * This type extends JavaScript's `Iterator`, and so has the iterator helper
+ * methods. It may extend the upcoming TypeScript `Iterator` class in the future.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
+ * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
+ */
+export declare class Fib3 extends Iterator<number, void, number> {
   current: number
-  next: number
-  constructor(current: number, next: number)
-  [Symbol.iterator](): Iterator<number, void, number>
+  nextNum: number
+  constructor(current: number, nextNum: number)
 }
 
 export declare class GetterSetterWithClosures {
diff --git a/examples/napi/src/generator.rs b/examples/napi/src/generator.rs
index b3b0ca0ec6..0ed81b6f89 100644
--- a/examples/napi/src/generator.rs
+++ b/examples/napi/src/generator.rs
@@ -84,7 +84,7 @@ impl Fib2 {
 #[napi(iterator, constructor)]
 pub struct Fib3 {
   pub current: u32,
-  pub next: u32,
+  pub next_num: u32,
 }
 
 #[napi]
@@ -97,13 +97,13 @@ impl Generator for Fib3 {
     match value {
       Some(n) => {
         self.current = n as u32;
-        self.next = n as u32 + 1;
+        self.next_num = n as u32 + 1;
       }
       None => {
-        let next = self.next;
+        let next = self.next_num;
         let current = self.current;
         self.current = next;
-        self.next = current + next;
+        self.next_num = current + next;
       }
     };
     Some(self.current)
diff --git a/tsconfig.json b/tsconfig.json
index e093de87ad..c43da4e305 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,7 +22,7 @@
     "resolveJsonModule": true,
     "importsNotUsedAsValues": "remove",
     "outDir": "scripts",
-    "lib": ["ES2022"]
+    "lib": ["ESNext"]
   },
   "include": [],
   "references": [


Test patch:
diff --git a/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md b/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md
index b0004c24c0..787c97147f 100644
--- a/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md
+++ b/cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md
@@ -197,8 +197,8 @@ Generated by [AVA](https://avajs.dev).
     ␊
     export declare class Fib3 {␊
       current: number␊
-      next: number␊
-      constructor(current: number, next: number)␊
+      nextNum: number␊
+      constructor(current: number, nextNum: number)␊
       [Symbol.iterator](): Iterator<number, void, number>␊
     }␊
     ␊
diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.md b/examples/napi/__tests__/__snapshots__/values.spec.ts.md
index 74bd810e8e..5f550d1a56 100644
--- a/examples/napi/__tests__/__snapshots__/values.spec.ts.md
+++ b/examples/napi/__tests__/__snapshots__/values.spec.ts.md
@@ -214,21 +214,39 @@ Generated by [AVA](https://avajs.dev).
       constructor(name: string)␊
     }␊
     ␊
-    export declare class Fib {␊
-      [Symbol.iterator](): Iterator<number, void, number>␊
+    /**␊
+     * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
+     * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
+     *␊
+     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
+     * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
+     */␊
+    export declare class Fib extends Iterator<number, void, number> {␊
       constructor()␊
     }␊
     ␊
-    export declare class Fib2 {␊
-      [Symbol.iterator](): Iterator<number, void, number>␊
+    /**␊
+     * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
+     * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
+     *␊
+     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
+     * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
+     */␊
+    export declare class Fib2 extends Iterator<number, void, number> {␊
       static create(seed: number): Fib2␊
     }␊
     ␊
-    export declare class Fib3 {␊
+    /**␊
+     * This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
+     * methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
+     *␊
+     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
+     * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
+     */␊
+    export declare class Fib3 extends Iterator<number, void, number> {␊
       current: number␊
-      next: number␊
-      constructor(current: number, next: number)␊
-      [Symbol.iterator](): Iterator<number, void, number>␊
+      nextNum: number␊
+      constructor(current: number, nextNum: number)␊
     }␊
     ␊
     export declare class GetterSetterWithClosures {␊
diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap
index 83179e042d..a6b84ba61a 100644
Binary files a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap differ
diff --git a/examples/napi/__tests__/generator.spec.ts b/examples/napi/__tests__/generator.spec.ts
index c7c52c9472..a63d8f61cd 100644
--- a/examples/napi/__tests__/generator.spec.ts
+++ b/examples/napi/__tests__/generator.spec.ts
@@ -12,10 +12,10 @@ for (const [index, factory] of [
   () => new Fib3(0, 1),
 ].entries()) {
   test(`should be able to stop a generator #${index}`, (t) => {
-    const fib = factory()
-    const gen = fib[Symbol.iterator]
-    t.is(typeof gen, 'function')
-    const iterator = gen()
+    let iterator = factory()
+    if (typeof Iterator === 'undefined') {
+      iterator = iterator[Symbol.iterator]()
+    }
     t.deepEqual(iterator.next(), {
       done: false,
       value: 1,
@@ -37,10 +37,7 @@ for (const [index, factory] of [
   })
 
   test(`should be able to throw to generator #${index}`, (t) => {
-    const fib = factory()
-    const gen = fib[Symbol.iterator]
-    t.is(typeof gen, 'function')
-    const iterator = gen()
+    const iterator = factory()
     t.deepEqual(iterator.next(), {
       done: false,
       value: 1,
@@ -58,4 +55,21 @@ for (const [index, factory] of [
       done: true,
     })
   })
+
+  test(`should be an Iterator and have the Iterator Helper methods #${index}`, (t) => {
+    if (typeof Iterator === 'undefined') {
+      t.pass('Iterator is not existing, skipping test')
+      return
+    }
+    const iterator = factory()
+
+    t.true(Object.getPrototypeOf(iterator) === Iterator.prototype)
+    let arr = [
+      ...iterator
+        .drop(3)
+        .filter((x: number) => x % 2 == 0)
+        .take(5),
+    ]
+    t.deepEqual(arr, [8, 34, 144, 610, 2584])
+  })
 }