Open treykasada opened 3 years ago
Currently using this as a workaround:
import * as f from "funtypes";
import { RuntypeBase } from "funtypes/lib/runtype";
import { RecordFields } from "funtypes/lib/types/Object";
type IntersectionFromUnion<Union> = (
Union extends any ? (_: Union) => void : never
) extends (_: infer Intersection) => void
? Intersection
: never;
type StaticSerialisedObject<
TypesByField extends RecordFields,
IsPartial extends boolean,
IsReadonly extends boolean
> = IsPartial extends false
? IsReadonly extends false
? { -readonly [K in keyof TypesByField]: StaticSerialised<TypesByField[K]> }
: { readonly [K in keyof TypesByField]: StaticSerialised<TypesByField[K]> }
: IsReadonly extends false
? { -readonly [K in keyof TypesByField]?: StaticSerialised<TypesByField[K]> }
: { readonly [K in keyof TypesByField]?: StaticSerialised<TypesByField[K]> };
type StaticSerialisedUnion<
MemberTypes extends readonly RuntypeBase<any>[]
> = StaticSerialised<Extract<MemberTypes[number], RuntypeBase<any>>>;
type StaticSerialisedIntersection<
MemberTypes extends readonly RuntypeBase<any>[]
> = IntersectionFromUnion<StaticSerialisedUnion<MemberTypes>>;
export type StaticSerialised<
Funtype extends RuntypeBase<any>
> = Funtype extends f.ReadonlyArray<infer ElementType>
? readonly StaticSerialised<ElementType>[]
: Funtype extends f.Array<infer ElementType>
? StaticSerialised<ElementType>[]
: Funtype extends f.Partial<infer TypesByField, infer IsReadonly>
? StaticSerialisedObject<TypesByField, true, IsReadonly>
: Funtype extends f.Object<infer TypesByField, infer IsReadonly>
? StaticSerialisedObject<TypesByField, false, IsReadonly>
: Funtype extends f.Union<infer MemberTypes>
? StaticSerialisedUnion<MemberTypes>
: Funtype extends f.Intersect<infer MemberTypes>
? StaticSerialisedIntersection<MemberTypes>
: Funtype extends f.ParsedValue<infer SourceType, any>
? StaticSerialised<SourceType>
: Funtype extends f.Constraint<infer SourceType>
? StaticSerialised<SourceType>
: Funtype extends f.Brand<any, infer SourceType>
? StaticSerialised<SourceType>
: Funtype extends f.Codec<infer CodecType>
? CodecType
: never;
export const serialise = <Funtype extends f.Codec<any>>(
funtype: Funtype,
value: f.Static<Funtype>
): StaticSerialised<Funtype> =>
funtype.serialize(value) as StaticSerialised<Funtype>;
export const safeSerialise = <Funtype extends f.Codec<any>>(
funtype: Funtype,
value: f.Static<Funtype>
): f.Result<StaticSerialised<Funtype>> =>
funtype.safeSerialize(value) as f.Result<StaticSerialised<Funtype>>;
Seems to work, but it's a bit of a mess, and likely not as performant (in terms of build times) as the approach used by funtypes
to derive the decoded/parsed type (i.e. Static
). 😅
Looks like I came to just about the same solution. 😖 Should have checked here first (I checked a while ago but this answer is new!)
export type ToWireType<T> =
T extends t.Intersect<infer U> ? UnionToIntersection<{ [I in keyof U]: ToWireType<U[I]> }[number]>
: T extends t.Union<infer U> ? { [I in keyof U]: ToWireType<U[I]> }[number]
: T extends t.Partial<infer U, infer V> ? V extends true ? { readonly [K in keyof U]?: ToWireType<U[K]> } : { [K in keyof U]?: ToWireType<U[K]> }
: T extends t.Object<infer U, infer V> ? V extends true ? { readonly [K in keyof U]: ToWireType<U[K]> } : { [K in keyof U]: ToWireType<U[K]> }
: T extends t.ReadonlyArray<infer U> ? readonly ToWireType<U>[]
: T extends t.Array<infer U> ? ToWireType<U>[]
: T extends t.ParsedValue<infer U, infer _> ? ToWireType<U>
: T extends t.Codec<infer U> ? U
: never
Likely incomplete (comparing with above it appears I'm missing Brand and Constraint at least). One nice thing about this solution is it doesn't require a dependency on internal RuntypeBase
so it can be used without an update to funtypes
. That being said, it would be great if funtypes
integrated one of these solutions directly into the serialize functions so we don't have to hack around it!
17 essentially transformed funtypes into codecs, able to both decode/parse and encode/serialise values.
The existing
Static
type function allows us to derive the decoded/parsed type from a funtype. It would be really useful to also have a way of deriving the encoded/serialised type. 🙂The primary usecase I have is including data defined by funtypes describing non-JSON-representable values in statically-typed JSON API response payloads. E.g.
I took a look at the existing type definitions, and it looks like the main blocker for this is that
Codec
doesn't currently keep track of the serialised type like it does for the parsed type. Hopefully not too hard to add though.