aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-12-14 19:09:01 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-12-14 19:09:01 +0100
commita0b5aba71c6e1a9fc5fba83fea0cfb1f9fe0b9f2 (patch)
tree848b76c72190f469523dd5db1d07b7b22fd21eaa
parent47b2b841357dd723be0144ff4ae9eab51bba37a0 (diff)
allow specifying common base type for unions
-rw-r--r--src/util/codec.ts48
1 files changed, 34 insertions, 14 deletions
diff --git a/src/util/codec.ts b/src/util/codec.ts
index 7dcf493a2..0215ce797 100644
--- a/src/util/codec.ts
+++ b/src/util/codec.ts
@@ -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>();
- constructor(private discriminator: D) {}
+ constructor(private discriminator: D, private baseCodec?: Codec<B>) {}
/**
* Define a property for the object.
@@ -129,7 +129,7 @@ class UnionCodecBuilder<T, D extends keyof T, TC> {
alternative<V>(
tagValue: T[D],
codec: Codec<V>,
- ): UnionCodecBuilder<T, D, TC | V> {
+ ): UnionCodecBuilder<T, D, B, TC | V> {
this.alternatives.set(tagValue, { codec, tagValue });
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,
* 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 discriminator = this.discriminator;
+ const baseCodec = this.baseCodec;
return {
decode(x: any, c?: Context): R {
const d = x[discriminator];
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);
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) {
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.
@@ -234,8 +251,11 @@ export function mapCodec<T>(innerCodec: Codec<T>): Codec<{ [x: string]: T }> {
}
export class UnionCodecPreBuilder<T> {
- discriminateOn<D extends keyof T>(discriminator: D): UnionCodecBuilder<T, D, never> {
- return new UnionCodecBuilder<T, D, never>(discriminator);
+ discriminateOn<D extends keyof T, B>(
+ discriminator: D,
+ baseCodec?: Codec<B>,
+ ): UnionCodecBuilder<T, D, B, never> {
+ return new UnionCodecBuilder<T, D, B, never>(discriminator, baseCodec);
}
}