diff --git a/macros/src/attr/enum.rs b/macros/src/attr/enum.rs index e70ba4b5..0f6796df 100644 --- a/macros/src/attr/enum.rs +++ b/macros/src/attr/enum.rs @@ -245,6 +245,15 @@ impl Attr for EnumAttr { item; "content cannot be used without tag" ), + (false, Some(_), None) => { + for variant in item.variants.iter() { + if let Fields::Unnamed(ref unnamed) = variant.fields { + if unnamed.unnamed.len() > 1 { + syn_err_spanned!(variant; r#"`#[ts(tag = "...")]` cannot be used with tuple variants"#); + } + } + } + } _ => (), }; diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index 2366e886..90ca954f 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -139,7 +139,7 @@ fn format_variant( }; let formatted = match (untagged_variant, enum_attr.tagged()?) { - (true, _) | (_, Tagged::Untagged) => quote!(#parsed_ty), + (true, _) | (_, Tagged::Untagged) => parsed_ty, (false, Tagged::Externally) => match &variant.fields { Fields::Unit => quote!(format!("\"{}\"", #ts_name)), Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { @@ -183,36 +183,29 @@ fn format_variant( format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #parsed_ty) ), }, - (false, Tagged::Internally { tag }) => match variant_type.inline_flattened { - Some(_) => { - quote! { #parsed_ty } - } - None => match &variant.fields { - Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { - let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; - - field_attr.assert_validity(field)?; - - if field_attr.skip { - quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)) - } else { - let ty = match field_attr.type_override { - Some(type_override) => quote! { #type_override }, - None => { - let ty = field_attr.type_as(&field.ty); - quote!(<#ty as #crate_rename::TS>::name(cfg)) - } - }; - - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)) - } - } - Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), - _ => { - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #parsed_ty)) + (false, Tagged::Internally { tag }) => match &variant.fields { + Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { + let field = &unnamed.unnamed[0]; + let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + + field_attr.assert_validity(field)?; + + if field_attr.skip { + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)) + } else { + let ty = match field_attr.type_override { + Some(type_override) => quote! { #type_override }, + None => { + let ty = field_attr.type_as(&field.ty); + quote!(<#ty as #crate_rename::TS>::name(cfg)) + } + }; + + quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)) } - }, + } + Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), + _ => parsed_ty, }, }; diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index 2a95b502..cf6f4b80 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -13,7 +13,7 @@ pub(crate) fn empty_object(attr: &StructAttr, ts_name: Expr) -> DerivedTS { DerivedTS { crate_rename: crate_rename.clone(), inline: quote!("Record".to_owned()), - inline_flattened: None, + inline_flattened: Some(quote!("{ }".to_owned())), docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -51,7 +51,7 @@ pub(crate) fn null(attr: &StructAttr, ts_name: Expr) -> DerivedTS { DerivedTS { crate_rename: crate_rename.clone(), inline: quote!("null".to_owned()), - inline_flattened: None, + inline_flattened: Some(quote!("{ }".to_owned())), docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, diff --git a/ts-rs/tests/integration/flatten.rs b/ts-rs/tests/integration/flatten.rs index 8bb3a423..486f2471 100644 --- a/ts-rs/tests/integration/flatten.rs +++ b/ts-rs/tests/integration/flatten.rs @@ -29,6 +29,19 @@ struct C { d: i32, } +#[derive(TS)] +#[ts(export, export_to = "flatten/")] +pub struct Inner {} + +// Create a parent struct that flattens the zero-field struct: +#[derive(TS)] +#[ts(export, export_to = "flatten/")] +pub struct Outer { + #[ts(flatten)] + pub inner: Inner, + pub other_field: String, +} + #[test] fn test_def() { let cfg = Config::from_env(); @@ -36,4 +49,5 @@ fn test_def() { C::inline(&cfg), "{ b: { c: number, a: number, b: number, } & ({ [key in string]: number }), d: number, }" ); + assert_eq!(Outer::inline(&cfg), "{ other_field: string, }"); }