feat: make generator an iterator (#2784)

* make the generator an iterator

* add JSDoc for generators

* prevent naming conflicts

* add JSDoc struct

* update snapshot

* clippy

* Update typedef

* update fmt

* Apply code review

* test compatible

---------

Co-authored-by: LongYinan <lynweklm@gmail.com>
This commit is contained in:
Mikaël Francoeur 2025-08-07 04:03:37 -04:00 committed by GitHub
parent 1e30088198
commit 88fca29e3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 393 additions and 210 deletions

View File

@ -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>
}␊

View File

@ -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)

View File

@ -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)]

View File

@ -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,

View File

@ -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),
})
}
}

View File

@ -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 ")

View File

@ -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 {

View File

@ -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 {

View File

@ -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),
})
}
}

View File

@ -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,
}),
});
}

View File

@ -48,50 +48,193 @@ 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 next_function = ptr::null_mut();
check_status_or_throw!(
env,
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,
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,
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,
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,
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,
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,
sys::napi_get_boolean(env, false, &mut generator_state),
"Create generator state failed"
);
let properties = [sys::napi_property_descriptor {
utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
name: ptr::null_mut(),
method: None,
getter: None,
setter: None,
value: generator_state,
attributes: sys::PropertyAttributes::writable,
data: ptr::null_mut(),
}];
check_status_or_throw!(
env,
sys::napi_define_properties(env, instance, 1, properties.as_ptr()),
"Define properties on Generator object 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,
)
},
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) },
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)]
@ -117,122 +260,8 @@ pub unsafe extern "C" fn symbol_generator<T: Generator>(
},
"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,
)
},
"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"
);
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"
);
check_status_or_throw!(
env,
unsafe {
sys::napi_set_named_property(
env,
generator_object,
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,
)
},
"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,
)
},
"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) },
"Create generator state failed"
);
let properties = [sys::napi_property_descriptor {
utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
name: ptr::null_mut(),
method: None,
getter: None,
setter: None,
value: generator_state,
attributes: sys::PropertyAttributes::writable,
data: ptr::null_mut(),
}];
check_status_or_throw!(
env,
unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) },
"Define properties on Generator object failed"
);
generator_object
this
}
extern "C" fn generator_next<T: Generator>(

View File

@ -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 {␊

View File

@ -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])
})
}

View File

@ -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 {

View File

@ -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)

View File

@ -22,7 +22,7 @@
"resolveJsonModule": true,
"importsNotUsedAsValues": "remove",
"outDir": "scripts",
"lib": ["ES2022"]
"lib": ["ESNext"]
},
"include": [],
"references": [