-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathtsjson-parser.ts
51 lines (41 loc) · 1.41 KB
/
tsjson-parser.ts
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
44
45
46
47
48
49
50
51
import Ajv from "ajv";
import { InternalTypeSymbol, JsonValue, SchemaLike } 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) as T,
stringify: <T>(input: T): TsjsonString<T> =>
JSON.stringify(input) as TsjsonString<T>,
};
export type Validated<T extends SchemaLike> = T[typeof InternalTypeSymbol];
export class TsjsonParser<T extends SchemaLike> {
public readonly schema: T;
private readonly validator: Ajv.ValidateFunction;
constructor(schema: T, ajvOptions: Ajv.Options = {}) {
this.schema = schema;
const ajv = new Ajv(ajvOptions);
this.validator = ajv.compile(schema);
}
// call this to get the errors from the most recent validation call.
public getErrors = (): Ajv.ErrorObject[] | null | undefined =>
this.validator.errors;
public validates(data: unknown): data is Validated<T> {
const valid = this.validator(data);
if (!valid) {
return false;
}
return true;
}
public parse = (text: string, skipValidation = false): Validated<T> => {
const data: unknown = JSON.parse(text);
if (skipValidation) {
return data;
}
if (this.validates(data)) {
return data;
}
throw new Error(JSON.stringify(this.validator.errors));
};
}