diff --git a/tests/expand/generic_constrained_struct.expanded.rs b/tests/expand/generic_constrained_struct.expanded.rs new file mode 100644 index 0000000..ff72061 --- /dev/null +++ b/tests/expand/generic_constrained_struct.expanded.rs @@ -0,0 +1,498 @@ +use tsify::Tsify; +pub trait Constraint {} +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct GenericStruct { + x: T, +} +const _: () = { + extern crate serde as _serde; + use tsify::Tsify; + use wasm_bindgen::{ + convert::{ + FromWasmAbi, VectorFromWasmAbi, IntoWasmAbi, VectorIntoWasmAbi, + OptionFromWasmAbi, OptionIntoWasmAbi, RefFromWasmAbi, + }, + describe::WasmDescribe, describe::WasmDescribeVector, prelude::*, + }; + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(typescript_type = "GenericStruct")] + pub type JsType; + } + #[automatically_derived] + impl Tsify for GenericStruct { + type JsType = JsType; + const DECL: &'static str = "export interface GenericStruct {\n x: T;\n}"; + const SERIALIZATION_CONFIG: tsify::SerializationConfig = tsify::SerializationConfig { + missing_as_null: false, + hashmap_as_object: false, + large_number_types_as_bigints: false, + }; + } + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = "export interface GenericStruct {\n x: T;\n}"; + #[automatically_derived] + impl WasmDescribe for GenericStruct { + #[inline] + fn describe() { + ::JsType::describe() + } + } + #[automatically_derived] + impl WasmDescribeVector for GenericStruct { + #[inline] + fn describe_vector() { + ::JsType::describe_vector() + } + } + #[automatically_derived] + impl IntoWasmAbi for &GenericStruct + where + GenericStruct: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn into_abi(self) -> Self::Abi { + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } + } + } + #[automatically_derived] + impl IntoWasmAbi for GenericStruct + where + GenericStruct: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn into_abi(self) -> Self::Abi { + (&self).into_abi() + } + } + #[automatically_derived] + impl OptionIntoWasmAbi for GenericStruct + where + GenericStruct: _serde::Serialize, + { + #[inline] + fn none() -> Self::Abi { + ::none() + } + } + #[automatically_derived] + impl From> for JsValue + where + GenericStruct: _serde::Serialize, + { + #[inline] + fn from(value: GenericStruct) -> Self { + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } + } + } + #[automatically_derived] + impl VectorIntoWasmAbi for GenericStruct + where + GenericStruct: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi { + let values = vector + .iter() + .map(|value| match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + }) + .collect(); + JsValue::vector_into_abi(values) + } + } + #[automatically_derived] + impl FromWasmAbi for GenericStruct + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + #[inline] + unsafe fn from_abi(js: Self::Abi) -> Self { + let result = Self::from_js(&JsType::from_abi(js)); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + result.unwrap_throw() + } + } + #[automatically_derived] + impl OptionFromWasmAbi for GenericStruct + where + Self: _serde::de::DeserializeOwned, + { + #[inline] + fn is_none(js: &Self::Abi) -> bool { + ::is_none(js) + } + } + pub struct SelfOwner(T); + #[automatically_derived] + impl ::core::ops::Deref for SelfOwner { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + #[automatically_derived] + impl RefFromWasmAbi for GenericStruct + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + type Anchor = SelfOwner; + unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { + let result = Self::from_js(&*JsType::ref_from_abi(js)); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + SelfOwner(result.unwrap_throw()) + } + } + #[automatically_derived] + impl VectorFromWasmAbi for GenericStruct + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + #[inline] + unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> { + JsValue::vector_from_abi(js) + .into_iter() + .map(|value| { + let result = Self::from_js(value); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + result.unwrap_throw() + }) + .collect() + } + } +}; +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct GenericNewtype(T); +const _: () = { + extern crate serde as _serde; + use tsify::Tsify; + use wasm_bindgen::{ + convert::{ + FromWasmAbi, VectorFromWasmAbi, IntoWasmAbi, VectorIntoWasmAbi, + OptionFromWasmAbi, OptionIntoWasmAbi, RefFromWasmAbi, + }, + describe::WasmDescribe, describe::WasmDescribeVector, prelude::*, + }; + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(typescript_type = "GenericNewtype")] + pub type JsType; + } + #[automatically_derived] + impl Tsify for GenericNewtype { + type JsType = JsType; + const DECL: &'static str = "export type GenericNewtype = T;"; + const SERIALIZATION_CONFIG: tsify::SerializationConfig = tsify::SerializationConfig { + missing_as_null: false, + hashmap_as_object: false, + large_number_types_as_bigints: false, + }; + } + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = "export type GenericNewtype = T;"; + #[automatically_derived] + impl WasmDescribe for GenericNewtype { + #[inline] + fn describe() { + ::JsType::describe() + } + } + #[automatically_derived] + impl WasmDescribeVector for GenericNewtype { + #[inline] + fn describe_vector() { + ::JsType::describe_vector() + } + } + #[automatically_derived] + impl IntoWasmAbi for &GenericNewtype + where + GenericNewtype: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn into_abi(self) -> Self::Abi { + match self.into_js() { + Ok(js) => js.into_abi(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } + } + } + #[automatically_derived] + impl IntoWasmAbi for GenericNewtype + where + GenericNewtype: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn into_abi(self) -> Self::Abi { + (&self).into_abi() + } + } + #[automatically_derived] + impl OptionIntoWasmAbi for GenericNewtype + where + GenericNewtype: _serde::Serialize, + { + #[inline] + fn none() -> Self::Abi { + ::none() + } + } + #[automatically_derived] + impl From> for JsValue + where + GenericNewtype: _serde::Serialize, + { + #[inline] + fn from(value: GenericNewtype) -> Self { + match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + } + } + } + #[automatically_derived] + impl VectorIntoWasmAbi for GenericNewtype + where + GenericNewtype: _serde::Serialize, + { + type Abi = ::Abi; + #[inline] + fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi { + let values = vector + .iter() + .map(|value| match value.into_js() { + Ok(js) => js.into(), + Err(err) => { + let loc = core::panic::Location::caller(); + let msg = ::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "(Converting type failed) {0} ({1}:{2}:{3})", err, loc + .file(), loc.line(), loc.column(), + ), + ) + }); + { + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_const_panic_str] + #[rustc_do_not_const_check] + const fn panic_cold_display( + arg: &T, + ) -> ! { + ::core::panicking::panic_display(arg) + } + panic_cold_display(&msg); + }; + } + }) + .collect(); + JsValue::vector_into_abi(values) + } + } + #[automatically_derived] + impl FromWasmAbi for GenericNewtype + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + #[inline] + unsafe fn from_abi(js: Self::Abi) -> Self { + let result = Self::from_js(&JsType::from_abi(js)); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + result.unwrap_throw() + } + } + #[automatically_derived] + impl OptionFromWasmAbi for GenericNewtype + where + Self: _serde::de::DeserializeOwned, + { + #[inline] + fn is_none(js: &Self::Abi) -> bool { + ::is_none(js) + } + } + pub struct SelfOwner(T); + #[automatically_derived] + impl ::core::ops::Deref for SelfOwner { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + #[automatically_derived] + impl RefFromWasmAbi for GenericNewtype + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + type Anchor = SelfOwner; + unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { + let result = Self::from_js(&*JsType::ref_from_abi(js)); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + SelfOwner(result.unwrap_throw()) + } + } + #[automatically_derived] + impl VectorFromWasmAbi for GenericNewtype + where + Self: _serde::de::DeserializeOwned, + { + type Abi = ::Abi; + #[inline] + unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> { + JsValue::vector_from_abi(js) + .into_iter() + .map(|value| { + let result = Self::from_js(value); + if let Err(err) = result { + wasm_bindgen::throw_str(err.to_string().as_ref()); + } + result.unwrap_throw() + }) + .collect() + } + } +}; diff --git a/tests/expand/generic_constrained_struct.rs b/tests/expand/generic_constrained_struct.rs new file mode 100644 index 0000000..079db02 --- /dev/null +++ b/tests/expand/generic_constrained_struct.rs @@ -0,0 +1,13 @@ +use tsify::Tsify; + +pub trait Constraint {} + +#[derive(Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct GenericStruct { + x: T, +} + +#[derive(Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct GenericNewtype(T); diff --git a/tsify-macros/src/wasm_bindgen.rs b/tsify-macros/src/wasm_bindgen.rs index 039ab38..ac76aa2 100644 --- a/tsify-macros/src/wasm_bindgen.rs +++ b/tsify-macros/src/wasm_bindgen.rs @@ -97,12 +97,31 @@ fn expand_into_wasm_abi(cont: &Container) -> TokenStream { let ident = cont.ident(); let serde_path = cont.serde_container.attrs.serde_path(); let mut generics = cont.generics_without_defaults(); - let borrowed_generics = generics.clone(); + + // Remove all constraints from every type parameter because in the generated impls + let generics_without_bounds = syn::Generics { + params: generics + .params + .iter() + .cloned() + .map(|param| match param { + syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { + eq_token: None, + default: None, + colon_token: None, + bounds: syn::punctuated::Punctuated::new(), + ..param + }), + _ => param, + }) + .collect(), + ..generics.clone() + }; generics .make_where_clause() .predicates - .push(parse_quote!(#ident #borrowed_generics: #serde_path::Serialize)); + .push(parse_quote!(#ident #generics_without_bounds: #serde_path::Serialize)); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();