All files tsjson.ts

100% Statements 18/18
100% Branches 5/5
100% Functions 5/5
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 441x     1x           1x 1x     1x         1x       13x 13x 13x       17x 17x 5x       13x 19x 19x 2x   17x 13x      
import Ajv from "ajv";
 
import { InternalTypeSymbol, JsonSchema, JsonValue } from "./json-schema";
const hiddenField = Symbol("SpecialTypeAnnotationFieldDoNotUse");
 
type TsjsonString<T> = string & {
  [hiddenField]: T extends JsonValue ? T : unknown;
};
 
export const TSJSON = {
  parse: <T>(input: TsjsonString<T>): T => JSON.parse(input),
 
  stringify: <T>(input: T): TsjsonString<T> =>
    JSON.stringify(input) as TsjsonString<T>
};
 
export type Validated<T extends JsonSchema> = T[typeof InternalTypeSymbol];
 
export class TsjsonParser<T extends JsonSchema> {
  public readonly schema: T;
  private readonly validator: Ajv.ValidateFunction;
  constructor(schema: T) {
    this.schema = schema;
    const ajv = new Ajv();
    this.validator = ajv.compile(schema);
  }
 
  public validate(data: unknown): asserts data is Validated<T> {
    const valid = this.validator(data);
    if (!valid) {
      throw new Error(JSON.stringify(this.validator.errors));
    }
  }
 
  public parse = (text: string, skipValidation = false): Validated<T> => {
    const data = JSON.parse(text);
    if (skipValidation) {
      return data;
    }
    this.validate(data);
    return data;
  };
}