#![allow(clippy::box_collection, clippy::enum_variant_names, dead_code)] #![allow(dead_code)] use std::{ collections::{BTreeMap, HashSet}, fmt::Debug, rc::Rc, }; use ts_rs::TS; #[derive(TS)] #[ts(export, export_to = "generics/")] struct Generic where T: TS, { value: T, values: Vec, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericAutoBound { value: T, values: Vec, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericAutoBound2 where T: PartialEq, { value: T, values: Vec, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct Container { foo: Generic, bar: Box>>, baz: Box>>>, } macro_rules! declare { ($(#[$meta:meta])* $name:ident { $($fident:ident: $t:ty),+ $(,)? }) => { $(#[$meta])* struct $name { $(pub $fident: $t),+ } } } declare! { #[derive(TS)] #[ts(export, export_to = "generics/")] TypeGroup { foo: Vec, } } #[test] fn test() { assert_eq!( TypeGroup::decl(), "type TypeGroup = { foo: Array, };", ); assert_eq!( Generic::<()>::decl(), "type Generic = { value: T, values: Array, };" ); assert_eq!( GenericAutoBound::<()>::decl(), "type GenericAutoBound = { value: T, values: Array, };" ); assert_eq!( GenericAutoBound2::<()>::decl(), "type GenericAutoBound2 = { value: T, values: Array, };" ); assert_eq!( Container::decl(), "type Container = { foo: Generic, bar: Array>, baz: { [key: string]: Generic }, };" ); } #[derive(TS)] #[ts(export, export_to = "generics/")] enum GenericEnum { A(A), B(B, B, B), C(Vec), D(Vec>>), E { a: A, b: B, c: C }, X(Vec), Y(i32), Z(Vec>), } #[test] fn generic_enum() { assert_eq!( GenericEnum::<(), (), ()>::decl(), r#"type GenericEnum = { "A": A } | { "B": [B, B, B] } | { "C": Array } | { "D": Array>> } | { "E": { a: A, b: B, c: C, } } | { "X": Array } | { "Y": number } | { "Z": Array> };"# ) } #[derive(TS)] #[ts(export, export_to = "generics/")] struct NewType(Vec>); #[test] fn generic_newtype() { assert_eq!( NewType::<()>::decl(), r#"type NewType = Array>;"# ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct Tuple(T, Vec, Vec>); #[test] fn generic_tuple() { assert_eq!( Tuple::<()>::decl(), r#"type Tuple = [T, Array, Array>];"# ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct Struct { a: T, b: (T, T), c: (T, (T, T)), d: [T; 3], e: [(T, T); 3], f: Vec, g: Vec>, h: Vec<[(T, T); 3]>, } #[test] fn generic_struct() { assert_eq!( Struct::<()>::decl(), "type Struct = { a: T, b: [T, T], c: [T, [T, T]], d: [T, T, T], e: [[T, T], [T, T], [T, T]], f: Array, g: Array>, h: Array<[[T, T], [T, T], [T, T]]>, };" ) } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericInline { t: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ContainerInline { g: GenericInline, #[ts(inline)] gi: GenericInline, #[ts(flatten)] t: GenericInline>, } #[test] fn inline() { assert_eq!( GenericInline::<()>::decl(), "type GenericInline = { t: T, };" ); assert_eq!( ContainerInline::decl(), "type ContainerInline = { g: GenericInline, gi: { t: string, }, t: Array, };" ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericWithBounds { t: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ContainerWithBounds { g: GenericWithBounds, #[ts(inline)] gi: GenericWithBounds, #[ts(flatten)] t: GenericWithBounds, } #[test] fn inline_with_bounds() { assert_eq!( GenericWithBounds::<&'static str>::decl(), "type GenericWithBounds = { t: T, };" ); assert_eq!( ContainerWithBounds::decl(), "type ContainerWithBounds = { g: GenericWithBounds, gi: { t: string, }, t: number, };" ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericWithDefault { t: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ContainerWithDefault { g: GenericWithDefault, #[ts(inline)] gi: GenericWithDefault, #[ts(flatten)] t: GenericWithDefault, } #[test] fn inline_with_default() { assert_eq!( GenericWithDefault::<()>::decl(), "type GenericWithDefault = { t: T, };" ); assert_eq!( ContainerWithDefault::decl(), "type ContainerWithDefault = { g: GenericWithDefault, gi: { t: string, }, t: number, };" ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ADefault { t: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct BDefault>> { u: U, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct YDefault { a1: ADefault, a2: ADefault, } #[test] fn default() { assert_eq!( ADefault::<()>::decl(), "type ADefault = { t: T, };" ); assert_eq!( BDefault::<()>::decl(), "type BDefault | null> = { u: U, };" ); assert!(BDefault::<()>::dependencies() .iter() .any(|dep| dep.ts_name == "ADefault")); assert_eq!( YDefault::decl(), "type YDefault = { a1: ADefault, a2: ADefault, };" ) } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ATraitBounds { t: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct BTraitBounds(T); #[derive(TS)] #[ts(export, export_to = "generics/")] enum CTraitBounds { A { t: T }, B(T), C, D(T, K), } // Types with const generics can't be exported #[derive(TS)] struct DTraitBounds { t: [T; N], } #[test] fn trait_bounds() { assert_eq!( ATraitBounds::::decl(), "type ATraitBounds = { t: T, };" ); assert_eq!( BTraitBounds::<&'static str>::decl(), "type BTraitBounds = T;" ); assert_eq!( CTraitBounds::<&'static str, i32>::decl(), r#"type CTraitBounds = { "A": { t: T, } } | { "B": T } | "C" | { "D": [T, K] };"# ); let ty = format!( "type DTraitBounds = {{ t: [{}], }};", "T, ".repeat(41).trim_end_matches(", ") ); assert_eq!(DTraitBounds::<&str, 41>::decl(), ty) } #[derive(TS)] #[ts(export, export_to = "generics/")] struct T0 { t0: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct P0 { p0: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct T1 { t0: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct P1 { p0: T, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct Parent { a: T1>, b: T1>>>, c: T1>, } #[derive(TS)] #[ts(export, export_to = "generics/")] struct GenericParent { a_t: T1>, b_t: T1>>>, c_t: T1>, a_null: T1>, b_null: T1>>>, c_null: T1>, } #[test] fn deeply_nested() { assert_eq!( Parent::inline(), "{ a: T1>, b: T1>>>, c: T1>, }" ); assert_eq!( GenericParent::<()>::decl(), "type GenericParent = { \ a_t: T1>, \ b_t: T1>>>, \ c_t: T1>, \ a_null: T1>, \ b_null: T1>>>, \ c_null: T1>, \ };" ); } #[derive(TS)] #[ts(export, export_to = "generics/")] struct SomeType(String); #[derive(TS)] #[ts(export, export_to = "generics/")] enum MyEnum { VariantA(A), VariantB(B), } #[derive(TS)] #[ts(export, export_to = "generics/")] struct ParentEnum { e: MyEnum, #[ts(inline)] e1: MyEnum, } #[test] fn inline_generic_enum() { // This fails! // The #[ts(inline)] seems to inline recursively, so not only the definition of `MyEnum`, but // also the definition of `SomeType`. assert_eq!( ParentEnum::decl(), "type ParentEnum = { \ e: MyEnum, \ e1: { \"VariantA\": number } | { \"VariantB\": SomeType }, \ };" ); }