Repository: Aleph-Alpha/ts-rs (rust)

Issue:
Type overrides ignored for newtype enum variants in internal and adjacent representations
Hi,
Thanks for your work on ts-rs.

Currently the `#[ts(type = "something")]` overrides are not used for newtype variants for enums that use internal or adjacent representations:

```rs
#[derive(Serialize)]
struct S;

// compiles
#[derive(TS, Serialize)]
enum External {
    Newtype(#[ts(type = "unknown")] S),
}

// error[E0277]: the trait bound `S: TS` is not satisfied
#[derive(TS, Serialize)]
#[serde(tag = "tag")]
enum Internal {
    Newtype(#[ts(type = "unknown")] S),
}

// error[E0277]: the trait bound `S: TS` is not satisfied
#[derive(TS, Serialize)]
#[serde(tag = "tag", content = "content")]
enum Adjacent {
    Newtype(#[ts(type = "unknown")] S),
}
```
The code that causes this behaviour (for adjacent enums) is at https://github.com/Aleph-Alpha/ts-rs/blob/d59ca01878ce03f7b62ff0be56b350808fd257ed/macros/src/types/enum.rs#L97-L100

The simplest fix would probably be to parse the field attributes and just output the override if there is one.

Pull request description:
Use type overrides for newtype enum variants in internal and adjacent representations
Closes #126 

I wrote this quickly so there may be some side-effects I haven't considered, or maybe ignoring all of the other attributes is still problematic.

Patch:
diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs
index d60e70b..043379a 100644
--- a/macros/src/types/enum.rs
+++ b/macros/src/types/enum.rs
@@ -3,7 +3,7 @@ use quote::{format_ident, quote};
 use syn::{Fields, Generics, ItemEnum, Variant};
 
 use crate::{
-    attr::{EnumAttr, StructAttr, Tagged, VariantAttr},
+    attr::{EnumAttr, FieldAttr, StructAttr, Tagged, VariantAttr},
     deps::Dependencies,
     types,
     types::generics::{format_generics, format_type},
@@ -95,7 +95,13 @@ fn format_variant(
         },
         Tagged::Adjacently { tag, content } => match &variant.fields {
             Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
-                let ty = format_type(&unnamed.unnamed[0].ty, dependencies, generics);
+                let FieldAttr { type_override, .. } =
+                    FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?;
+                let ty = if let Some(type_override) = type_override {
+                    quote! { #type_override }
+                } else {
+                    format_type(&unnamed.unnamed[0].ty, dependencies, generics)
+                };
                 quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #name, #content, #ty))
             }
             Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)),
@@ -114,7 +120,13 @@ fn format_variant(
             },
             None => match &variant.fields {
                 Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
-                    let ty = format_type(&unnamed.unnamed[0].ty, dependencies, generics);
+                    let FieldAttr { type_override, .. } =
+                        FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?;
+                    let ty = if let Some(type_override) = type_override {
+                        quote! { #type_override }
+                    } else {
+                        format_type(&unnamed.unnamed[0].ty, dependencies, generics)
+                    };
                     quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #name, #ty))
                 }
                 Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #name)),


Test patch:
diff --git a/ts-rs/tests/type_override.rs b/ts-rs/tests/type_override.rs
index aa57a80..41b8459 100644
--- a/ts-rs/tests/type_override.rs
+++ b/ts-rs/tests/type_override.rs
@@ -38,3 +38,29 @@ fn newtype() {
     assert_eq!(New1::inline(), r#"string"#);
     assert_eq!(New2::inline(), r#"string | null"#);
 }
+
+#[test]
+#[cfg(feature = "serde-compat")]
+fn enum_newtype_representations() {
+    // regression test for https://github.com/Aleph-Alpha/ts-rs/issues/126
+
+    use serde::Serialize;
+
+    #[derive(Serialize)]
+    struct S;
+
+    #[derive(TS, Serialize)]
+    #[serde(tag = "t")]
+    enum Internal {
+        Newtype(#[ts(type = "unknown")] S),
+    }
+
+    #[derive(TS, Serialize)]
+    #[serde(tag = "t", content = "c")]
+    enum Adjacent {
+        Newtype(#[ts(type = "unknown")] S),
+    }
+
+    assert_eq!(Internal::inline(), r#"{ "t": "Newtype" } & unknown"#);
+    assert_eq!(Adjacent::inline(), r#"{ "t": "Newtype", "c": unknown }"#);
+}