diff --git a/macros/src/types/generics.rs b/macros/src/types/generics.rs index 4766de970..beacc2959 100644 --- a/macros/src/types/generics.rs +++ b/macros/src/types/generics.rs @@ -40,7 +40,7 @@ pub fn format_generics(deps: &mut Dependencies, generics: &Generics) -> TokenStr pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generics) -> TokenStream { // If the type matches one of the generic parameters, just pass the identifier: - if let Some(generic_ident) = generics + if let Some(generic) = generics .params .iter() .filter_map(|param| match param { @@ -55,9 +55,26 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi && type_path.path.is_ident(&type_param.ident) ) }) - .map(|type_param| type_param.ident.to_string()) { - return quote!(#generic_ident.to_owned()); + let generic_ident = generic.ident.clone(); + let generic_ident_str = generic_ident.to_string(); + + if !generic.bounds.is_empty() || generic.default.is_some() { + return quote!(#generic_ident_str.to_owned()); + } + + return quote!( + match <#generic_ident>::name().as_str() { + // When exporting a generic, the default type used is `()`, + // which gives "null" when calling `.name()`. In this case, we + // want to preserve the type param's identifier as the name used + "null" => #generic_ident_str.to_owned(), + + // If name is not "null", a type has been provided, so we use its + // name instead + x => x.to_owned() + } + ); } // special treatment for arrays and tuples diff --git a/ts-rs/tests/generics.rs b/ts-rs/tests/generics.rs index b4f8183cd..961fffc0b 100644 --- a/ts-rs/tests/generics.rs +++ b/ts-rs/tests/generics.rs @@ -147,8 +147,6 @@ fn generic_struct() { } #[test] -#[ignore] -// https://github.com/Aleph-Alpha/ts-rs/issues/56 TODO fn inline() { #[derive(TS)] struct Generic { @@ -167,7 +165,57 @@ fn inline() { assert_eq!(Generic::<()>::decl(), "type Generic = { t: T, }"); assert_eq!( Container::decl(), - "type Container = { g: Generic, gi: { t: string }, t: string, }" + "type Container = { g: Generic, gi: { t: string, }, t: string, }" + ); +} + +#[test] +#[ignore = "We haven't figured out how to inline generics with bounds yet"] +fn inline_with_bounds() { + #[derive(TS)] + struct Generic { + t: T, + } + + #[derive(TS)] + struct Container { + g: Generic, + #[ts(inline)] + gi: Generic, + #[ts(flatten)] + t: Generic, + } + + assert_eq!(Generic::<&'static str>::decl(), "type Generic = { t: T, }"); + assert_eq!( + Container::decl(), + "type Container = { g: Generic, gi: { t: string, }, t: number, }" + // Actual output: { g: Generic, gi: { t: T, }, t: T, } + ); +} + +#[test] +#[ignore = "We haven't figured out how to inline generics with defaults yet"] +fn inline_with_default() { + #[derive(TS)] + struct Generic { + t: T, + } + + #[derive(TS)] + struct Container { + g: Generic, + #[ts(inline)] + gi: Generic, + #[ts(flatten)] + t: Generic, + } + + assert_eq!(Generic::<&'static str>::decl(), "type Generic = { t: T, }"); + assert_eq!( + Container::decl(), + "type Container = { g: Generic, gi: { t: string, }, t: number, }" + // Actual output: { g: Generic, gi: { t: T, }, t: T, } ); }