/** * @module tagged-types * Minimal runtime type checking with discriminated unions */ const DEFAULT_KEY = "__$ref__" as const; export type Tagged = TData & { [DEFAULT_KEY]: TTag; }; class TagBuilder { constructor(private tagValue: TTag) {} isMaybe = (value: unknown): value is Tagged => { return ( value !== null && value !== undefined && typeof value === "object" && DEFAULT_KEY in value && (value as any)[DEFAULT_KEY] === this.tagValue ); }; create = (data: TData): Tagged => { return { ...data, [DEFAULT_KEY]: this.tagValue, } as Tagged; }; unwrap = (value: Tagged): TData => { const { [DEFAULT_KEY]: _, ...data } = value; return data as TData; }; } export function tag(tagName: string) { return Object.freeze(new TagBuilder(tagName)); }