diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 084fa81..a5522ee 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -43,6 +43,7 @@ pub struct DefOpts<'a> { pub enum DataType { // Always inlined Any, + Unknown, Primitive(PrimitiveType), Literal(LiteralType), /// Either a `Set` or a `Vec` diff --git a/src/lang/ts/mod.rs b/src/lang/ts/mod.rs index d028ca5..5ba1c64 100644 --- a/src/lang/ts/mod.rs +++ b/src/lang/ts/mod.rs @@ -176,6 +176,7 @@ pub fn datatype(conf: &ExportConfig, typ: &DataType, type_map: &TypeMap) -> Outp pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &TypeMap) -> Output { Ok(match &typ { DataType::Any => ANY.into(), + DataType::Unknown => UNKNOWN.into(), DataType::Primitive(p) => { let ctx = ctx.with(PathItem::Type(p.to_rust_str().into())); match p { @@ -658,6 +659,7 @@ pub(crate) fn sanitise_type_name(ctx: ExportContext, loc: NamedLocation, ident: } const ANY: &str = "any"; +const UNKNOWN: &str = "unknown"; const NUMBER: &str = "number"; const STRING: &str = "string"; const BOOLEAN: &str = "boolean"; diff --git a/src/serde.rs b/src/serde.rs index 2398bde..e3c48e2 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -246,7 +246,7 @@ fn validate_internally_tag_enum_datatype( // TODO: Maybe make this a public utility? fn resolve_generics(mut dt: DataType, generics: &Vec<(GenericType, DataType)>) -> DataType { match dt { - DataType::Primitive(_) | DataType::Literal(_) | DataType::Any => dt, + DataType::Primitive(_) | DataType::Literal(_) | DataType::Any | DataType::Unknown => dt, DataType::List(v) => DataType::List(Box::new(resolve_generics(*v, generics))), DataType::Nullable(v) => DataType::Nullable(Box::new(resolve_generics(*v, generics))), DataType::Map(v) => DataType::Map(Box::new({ diff --git a/src/static_types.rs b/src/static_types.rs index 252d510..6cc87a7 100644 --- a/src/static_types.rs +++ b/src/static_types.rs @@ -1,8 +1,15 @@ +use std::fmt::Debug; + use crate::{DataType, DefOpts, Type}; -/// A type that is unconstructable but is typed as `any` in TypeScript. +/// Easily convert a non-Specta type into a Specta compatible type. +/// This will be typed as `any` in Typescript. +/// +/// WARNING: When used with `Option>`, Typescript will not prompt you about nullability checks as `any | null` is coalesced to `any` in Typescript. /// -/// This can be use like the following: +/// # Examples +/// +/// This can be used as a type override. /// ```rust /// use serde::Serialize; /// use specta::{Type, Any}; @@ -13,10 +20,100 @@ use crate::{DataType, DefOpts, Type}; /// pub field: String, /// } /// ``` -pub enum Any {} +/// +/// Or it can be used as a wrapper type. +/// ```rust +/// use serde::Serialize; +/// use specta::{Type, Any}; +/// +/// #[derive(Serialize, Type)] +/// pub struct Demo { +/// pub field: Any, +/// } +/// ``` +pub struct Any(T); -impl Type for Any { +impl Type for Any { fn inline(_: DefOpts, _: &[DataType]) -> DataType { DataType::Any } } + +impl Debug for Any { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Any").field(&self.0).finish() + } +} + +impl Clone for Any { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Any { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + T::serialize(&self.0, serializer) + } +} + +/// Easily convert a non-Specta type into a Specta compatible type. +/// This will be typed as `unknown` in Typescript. +/// +/// # Examples +/// +/// This can be used as a type override. +/// ```rust +/// use serde::Serialize; +/// use specta::{Type, Unknown}; +/// +/// #[derive(Serialize, Type)] +/// pub struct Demo { +/// #[specta(type = Unknown)] +/// pub field: String, +/// } +/// ``` +/// +/// Or it can be used as a wrapper type. +/// ```rust +/// use serde::Serialize; +/// use specta::{Type, Unknown}; +/// +/// #[derive(Serialize, Type)] +/// pub struct Demo { +/// pub field: Unknown, +/// } +/// ``` +pub struct Unknown(T); + +impl Type for Unknown { + fn inline(_: DefOpts, _: &[DataType]) -> DataType { + DataType::Unknown + } +} + +impl Debug for Unknown { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Any").field(&self.0).finish() + } +} + +impl Clone for Unknown { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Unknown { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + T::serialize(&self.0, serializer) + } +} diff --git a/tests/lib.rs b/tests/lib.rs index be90aa3..cc146a5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -17,6 +17,7 @@ mod reserved_keywords; mod selection; mod serde; mod sid; +mod static_types; mod transparent; pub mod ts; mod ts_rs; diff --git a/tests/static_types.rs b/tests/static_types.rs new file mode 100644 index 0000000..d2ad0af --- /dev/null +++ b/tests/static_types.rs @@ -0,0 +1,12 @@ +use specta::{Any, Unknown}; + +use crate::ts::assert_ts; + +#[test] +fn static_types() { + assert_ts!(Any, "any"); + assert_ts!(Unknown, "unknown"); + + assert_ts!(Any, "any"); + assert_ts!(Unknown, "unknown"); +}