@@ -14,25 +14,44 @@ export interface DecoderError {
1414}
1515
1616/**
17- * Defines a mapped type over an interface `A`. `DecoderObject<A>` is an
18- * interface that has all the keys or `A`, but each key's property type is
19- * mapped to a decoder for that type. This type is used when creating decoders
20- * for objects.
17+ * Helper type with no semantic meaning, used as part of a trick in
18+ * `DecoderObject` to distinguish between optional properties and properties
19+ * that may have a value of undefined, but aren't optional.
20+ */
21+ type HideUndefined < T > = { } ;
22+
23+ /**
24+ * Defines a mapped type over an interface `A`. This type is used when creating
25+ * decoders for objects.
26+ *
27+ * `DecoderObject<A>` is an interface that has all the properties or `A`, but
28+ * each property's type is mapped to a decoder for that type. If a property is
29+ * required in `A`, the decoder type is `Decoder<proptype>`. If a property is
30+ * optional in `A`, then that property is required in `DecoderObject<A>`, but
31+ * the decoder type is `OptionalDecoder<proptype> | Decoder<proptype>`.
32+ *
33+ * The `OptionalDecoder` type is only returned by the `optional` decoder.
2134 *
2235 * Example:
2336 * ```
24- * interface X {
37+ * interface ABC {
2538 * a: boolean;
26- * b: string;
39+ * b?: string;
40+ * c: number | undefined;
2741 * }
2842 *
29- * const decoderObject: DecoderObject<X> = {
30- * a: boolean(),
31- * b: string()
43+ * DecoderObject<ABC> === {
44+ * a: Decoder<boolean>;
45+ * b: OptionalDecoder<string> | Decoder<string>;
46+ * c: Decoder<number | undefined>;
3247 * }
3348 * ```
3449 */
35- export type DecoderObject < A > = { [ t in keyof A ] : Decoder < A [ t ] > } ;
50+ export type DecoderObject < T > = {
51+ [ P in keyof T ] -?: undefined extends { [ Q in keyof T ] : HideUndefined < T [ Q ] > } [ P ]
52+ ? OptionalDecoder < Exclude < T [ P ] , undefined > > | Decoder < Exclude < T [ P ] , undefined > >
53+ : Decoder < T [ P ] >
54+ } ;
3655
3756/**
3857 * Type guard for `DecoderError`. One use case of the type guard is in the
@@ -112,6 +131,7 @@ const prependAt = (newAt: string, {at, ...rest}: Partial<DecoderError>): Partial
112131 * things with a `Result` as with the decoder methods.
113132 */
114133export class Decoder < A > {
134+ readonly _kind = 'Decoder' ;
115135 /**
116136 * The Decoder class constructor is kept private to separate the internal
117137 * `decode` function from the external `run` function. The distinction
@@ -280,15 +300,17 @@ export class Decoder<A> {
280300 let obj : any = { } ;
281301 for ( const key in decoders ) {
282302 if ( decoders . hasOwnProperty ( key ) ) {
283- const r = decoders [ key ] . decode ( json [ key ] ) ;
284- if ( r . ok === true ) {
285- // tslint:disable-next-line:strict-type-predicates
286- if ( r . result !== undefined ) {
287- obj [ key ] = r . result ;
288- }
289- } else if ( json [ key ] === undefined ) {
303+ // hack: type as any to access the private `decode` method on OptionalDecoder
304+ const decoder : any = decoders [ key ] ;
305+ const r = decoder . decode ( json [ key ] ) ;
306+ if (
307+ ( r . ok === true && decoder . _kind === 'Decoder' ) ||
308+ ( r . ok === true && decoder . _kind === 'OptionalDecoder' && r . result !== undefined )
309+ ) {
310+ obj [ key ] = r . result ;
311+ } else if ( r . ok === false && json [ key ] === undefined ) {
290312 return Result . err ( { message : `the key '${ key } ' is required but was not present` } ) ;
291- } else {
313+ } else if ( r . ok === false ) {
292314 return Result . err ( prependAt ( `.${ key } ` , r . error ) ) ;
293315 }
294316 }
@@ -363,28 +385,6 @@ export class Decoder<A> {
363385 }
364386 } ) ;
365387
366- /**
367- * Decoder for values that may be `undefined`. This is primarily helpful for
368- * decoding interfaces with optional fields.
369- *
370- * Example:
371- * ```
372- * interface User {
373- * id: number;
374- * isOwner?: boolean;
375- * }
376- *
377- * const decoder: Decoder<User> = object({
378- * id: number(),
379- * isOwner: optional(boolean())
380- * });
381- * ```
382- */
383- static optional = < A > ( decoder : Decoder < A > ) : Decoder < undefined | A > =>
384- new Decoder < undefined | A > (
385- ( json : any ) => ( json === undefined ? Result . ok ( undefined ) : decoder . decode ( json ) )
386- ) ;
387-
388388 /**
389389 * Decoder that attempts to run each decoder in `decoders` and either succeeds
390390 * with the first successful decoder, or fails after all decoders have failed.
@@ -655,3 +655,34 @@ export class Decoder<A> {
655655 Result . andThen ( value => f ( value ) . decode ( json ) , this . decode ( json ) )
656656 ) ;
657657}
658+
659+ export class OptionalDecoder < A > {
660+ readonly _kind = 'OptionalDecoder' ;
661+
662+ private constructor (
663+ private decode : ( json : any ) => Result . Result < A | undefined , Partial < DecoderError > >
664+ ) { }
665+
666+ static optional = < A > ( decoder : Decoder < A > ) : OptionalDecoder < A > =>
667+ new OptionalDecoder (
668+ // hack: type decoder as any to access the private `decode` method on Decoder
669+ ( json : any ) => ( json === undefined ? Result . ok ( undefined ) : ( decoder as any ) . decode ( json ) )
670+ ) ;
671+
672+ map = < B > ( f : ( value : A ) => B ) : OptionalDecoder < B > =>
673+ new OptionalDecoder < B > ( ( json : any ) =>
674+ Result . map (
675+ ( value : A | undefined ) => ( value === undefined ? undefined : f ( value ) ) ,
676+ this . decode ( json )
677+ )
678+ ) ;
679+
680+ andThen = < B > ( f : ( value : A ) => Decoder < B > ) : OptionalDecoder < B > =>
681+ new OptionalDecoder < B > ( ( json : any ) =>
682+ Result . andThen (
683+ ( value : A | undefined ) =>
684+ value === undefined ? Result . ok ( undefined ) : ( f ( value ) as any ) . decode ( json ) ,
685+ this . decode ( json )
686+ )
687+ ) ;
688+ }
0 commit comments