import {ValidationFailure} from '../error' import {GetCastType, GetSrcType, Validator} from '../interface' import {Simplify, print} from '../util' export type GetTaggedUnionCast = Simplify<{ [C in keyof U]: GetCastType & {[T in F]: C} }[keyof U]> export type GetTaggedUnionSrc = Simplify<{ [C in keyof U]: GetSrcType & {[T in F]: C} }[keyof U]> export class TaggedUnion>> implements Validator, GetTaggedUnionSrc> { private wrongTagMessage: string constructor( public readonly tagField: F, public readonly variants: U ) { this.wrongTagMessage = `got {value}, but expected one of ${print(Object.keys(this.variants))}` } cast(value: any): ValidationFailure | GetTaggedUnionCast { let variant = this.getVariant(value) if (variant instanceof ValidationFailure) return variant let result = variant.cast(value) if (result instanceof ValidationFailure) return result return { [this.tagField]: value[this.tagField], ...result } } validate(value: unknown): ValidationFailure | undefined { let variant = this.getVariant(value) if (variant instanceof ValidationFailure) return variant return variant.validate(value) } private getVariant(object: any): Validator| ValidationFailure { if (typeof object != 'object' || !object) return new ValidationFailure(object, `{value} is not an object`) let tag = object[this.tagField] let variant = this.variants[tag] if (variant) return variant let failure = new ValidationFailure(tag, this.wrongTagMessage) failure.path.push(this.tagField) return failure } phantom(): GetTaggedUnionSrc { throw new Error() } }