diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 687f5748..140c0e26 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -79,6 +79,7 @@ pub struct NapiStructField { pub setter: bool, pub comments: Vec, pub skip_typescript: bool, + pub ts_type: Option, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index d1bee1ca..d09047bc 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -89,7 +89,10 @@ impl NapiStruct { if !f.setter { field_str.push_str("readonly ") } + let (arg, is_optional) = ty_to_ts_type(&f.ty, false); + let arg = f.ts_type.as_ref().map(|ty| ty.to_string()).unwrap_or(arg); + let sep = if is_optional { "?" } else { "" }; let arg = format!("{}{}: {}", &f.js_name, sep, arg); if self.kind == NapiStructKind::Constructor { diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index afd00aa6..e066ed6c 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -54,6 +54,7 @@ macro_rules! attrgen { (namespace, Namespace(Span, String, Span)), (ts_args_type, TsArgsType(Span, String, Span)), (ts_return_type, TsReturnType(Span, String, Span)), + (ts_type, TsType(Span, String, Span)), // impl later // (inspectable, Inspectable(Span)), diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 23c9c7e1..cae6fd52 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -602,6 +602,12 @@ impl ParseNapi for syn::Item { impl ParseNapi for syn::ItemFn { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { + if opts.ts_type().is_some() { + bail_span!( + self, + "#[napi] can't be applied to a function with #[napi(ts_type)]" + ); + } self.to_tokens(tokens); self.convert_to_ast(opts) } @@ -611,10 +617,11 @@ impl ParseNapi for syn::ItemStruct { if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() || opts.skip_typescript().is_some() + || opts.ts_type().is_some() { bail_span!( self, - "#[napi] can't be applied to a struct with #[napi(ts_args_type)] or #[napi(ts_return_type)] or #[napi(skip_typescript)]" + "#[napi] can't be applied to a struct with #[napi(ts_args_type)], #[napi(ts_return_type)], #[napi(skip_typescript)] or #[napi(ts_type)]" ); } let napi = self.convert_to_ast(opts); @@ -628,10 +635,11 @@ impl ParseNapi for syn::ItemImpl { if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() || opts.skip_typescript().is_some() + || opts.ts_type().is_some() { bail_span!( self, - "#[napi] can't be applied to impl with #[napi(ts_args_type)] or #[napi(ts_return_type)] or #[napi(skip_typescript)]" + "#[napi] can't be applied to impl with #[napi(ts_args_type)], #[napi(ts_return_type)], #[napi(skip_typescript)] or #[napi(ts_type)]" ); } // #[napi] macro will be remove from impl items after converted to ast @@ -643,10 +651,11 @@ impl ParseNapi for syn::ItemImpl { } impl ParseNapi for syn::ItemEnum { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { - if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() || opts.ts_type().is_some() + { bail_span!( self, - "#[napi] can't be applied to a enum with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + "#[napi] can't be applied to a enum with #[napi(ts_args_type)], #[napi(ts_return_type)] or #[napi(ts_type)]" ); } let napi = self.convert_to_ast(opts); @@ -657,10 +666,11 @@ impl ParseNapi for syn::ItemEnum { } impl ParseNapi for syn::ItemConst { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { - if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() || opts.ts_type().is_some() + { bail_span!( self, - "#[napi] can't be applied to a const with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + "#[napi] can't be applied to a const with #[napi(ts_args_type)], #[napi(ts_return_type)] or #[napi(ts_type)]" ); } let napi = self.convert_to_ast(opts); @@ -760,6 +770,7 @@ impl ConvertToAST for syn::ItemStruct { let ignored = field_opts.skip().is_some(); let readonly = field_opts.readonly().is_some(); let skip_typescript = field_opts.skip_typescript().is_some(); + let ts_type = field_opts.ts_type().map(|e| e.0.to_string()); fields.push(NapiStructField { name, @@ -769,6 +780,7 @@ impl ConvertToAST for syn::ItemStruct { setter: !(ignored || readonly), comments: extract_doc_comments(&field.attrs), skip_typescript, + ts_type, }) } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 61d797e9..7812868e 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -102,6 +102,10 @@ Generated by [AVA](https://avajs.dev). }␊ export function receiveStrictObject(strictObject: StrictObject): void␊ export function getStrFromObject(): void␊ + export interface TsTypeChanged {␊ + typeOverride: object␊ + typeOverrideOptional?: object␊ + }␊ export function asyncPlus100(p: Promise): Promise␊ /** This is an interface for package.json */␊ export interface PackageJson {␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 6da8ac63..90bb2d66 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index a747fe77..8c1f4e5c 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -92,6 +92,10 @@ export interface StrictObject { } export function receiveStrictObject(strictObject: StrictObject): void export function getStrFromObject(): void +export interface TsTypeChanged { + typeOverride: object + typeOverrideOptional?: object +} export function asyncPlus100(p: Promise): Promise /** This is an interface for package.json */ export interface PackageJson { diff --git a/examples/napi/src/object.rs b/examples/napi/src/object.rs index bab1e228..9e4720a2 100644 --- a/examples/napi/src/object.rs +++ b/examples/napi/src/object.rs @@ -76,3 +76,12 @@ pub fn get_str_from_object(env: Env) { obj.set("name", "value").unwrap(); assert_eq!(obj.get("name").unwrap(), Some("value")); } + +#[napi(object)] +pub struct TsTypeChanged { + #[napi(ts_type = "object")] + pub type_override: String, + + #[napi(ts_type = "object")] + pub type_override_optional: Option, +}