allow specifying common base type for unions
This commit is contained in:
parent
47b2b84135
commit
a0b5aba71c
@ -118,10 +118,10 @@ class ObjectCodecBuilder<T, TC> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnionCodecBuilder<T, D extends keyof T, TC> {
|
class UnionCodecBuilder<T, D extends keyof T, B, TC> {
|
||||||
private alternatives = new Map<any, Alternative>();
|
private alternatives = new Map<any, Alternative>();
|
||||||
|
|
||||||
constructor(private discriminator: D) {}
|
constructor(private discriminator: D, private baseCodec?: Codec<B>) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a property for the object.
|
* Define a property for the object.
|
||||||
@ -129,7 +129,7 @@ class UnionCodecBuilder<T, D extends keyof T, TC> {
|
|||||||
alternative<V>(
|
alternative<V>(
|
||||||
tagValue: T[D],
|
tagValue: T[D],
|
||||||
codec: Codec<V>,
|
codec: Codec<V>,
|
||||||
): UnionCodecBuilder<T, D, TC | V> {
|
): UnionCodecBuilder<T, D, B, TC | V> {
|
||||||
this.alternatives.set(tagValue, { codec, tagValue });
|
this.alternatives.set(tagValue, { codec, tagValue });
|
||||||
return this as any;
|
return this as any;
|
||||||
}
|
}
|
||||||
@ -140,21 +140,36 @@ class UnionCodecBuilder<T, D extends keyof T, TC> {
|
|||||||
* @param objectDisplayName name of the object that this codec operates on,
|
* @param objectDisplayName name of the object that this codec operates on,
|
||||||
* used in error messages.
|
* used in error messages.
|
||||||
*/
|
*/
|
||||||
build<R extends TC>(objectDisplayName: string): Codec<R> {
|
build<R extends TC & B>(objectDisplayName: string): Codec<R> {
|
||||||
const alternatives = this.alternatives;
|
const alternatives = this.alternatives;
|
||||||
const discriminator = this.discriminator;
|
const discriminator = this.discriminator;
|
||||||
|
const baseCodec = this.baseCodec;
|
||||||
return {
|
return {
|
||||||
decode(x: any, c?: Context): R {
|
decode(x: any, c?: Context): R {
|
||||||
const d = x[discriminator];
|
const d = x[discriminator];
|
||||||
if (d === undefined) {
|
if (d === undefined) {
|
||||||
throw new DecodingError(`expected tag for ${objectDisplayName} at ${renderContext(c)}.${discriminator}`);
|
throw new DecodingError(
|
||||||
|
`expected tag for ${objectDisplayName} at ${renderContext(
|
||||||
|
c,
|
||||||
|
)}.${discriminator}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const alt = alternatives.get(d);
|
const alt = alternatives.get(d);
|
||||||
if (!alt) {
|
if (!alt) {
|
||||||
throw new DecodingError(`unknown tag for ${objectDisplayName} ${d} at ${renderContext(c)}.${discriminator}`);
|
throw new DecodingError(
|
||||||
|
`unknown tag for ${objectDisplayName} ${d} at ${renderContext(
|
||||||
|
c,
|
||||||
|
)}.${discriminator}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return alt.codec.decode(x);
|
const altDecoded = alt.codec.decode(x);
|
||||||
}
|
if (baseCodec) {
|
||||||
|
const baseDecoded = baseCodec.decode(x, c);
|
||||||
|
return { ...baseDecoded, ...altDecoded };
|
||||||
|
} else {
|
||||||
|
return altDecoded;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,10 +195,12 @@ export function stringConstCodec<V extends string>(s: V): Codec<V> {
|
|||||||
if (x === s) {
|
if (x === s) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
throw new DecodingError(`expected string constant "${s}" at ${renderContext(c)}`);
|
throw new DecodingError(
|
||||||
}
|
`expected string constant "${s}" at ${renderContext(c)}`,
|
||||||
}
|
);
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a codec for a value that must be a number.
|
* Return a codec for a value that must be a number.
|
||||||
@ -234,8 +251,11 @@ export function mapCodec<T>(innerCodec: Codec<T>): Codec<{ [x: string]: T }> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UnionCodecPreBuilder<T> {
|
export class UnionCodecPreBuilder<T> {
|
||||||
discriminateOn<D extends keyof T>(discriminator: D): UnionCodecBuilder<T, D, never> {
|
discriminateOn<D extends keyof T, B>(
|
||||||
return new UnionCodecBuilder<T, D, never>(discriminator);
|
discriminator: D,
|
||||||
|
baseCodec?: Codec<B>,
|
||||||
|
): UnionCodecBuilder<T, D, B, never> {
|
||||||
|
return new UnionCodecBuilder<T, D, B, never>(discriminator, baseCodec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user