Last active
November 16, 2024 06:19
-
-
Save fsubal/bc16412ffd169b2406065da0064f712c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ajv, { JSONSchemaType, ErrorObject } from 'ajv' | |
import { FromSchema } from 'json-schema-to-ts' | |
interface AggregatedValidationError extends AggregateError { | |
errors: ErrorObject[] | |
} | |
/** | |
* ajvは全スキーマをキャッシュするので、インスタンスごとにnewせず、使い回す | |
*/ | |
const ajv = new Ajv({ strict: true }) | |
type SafeParseResult<T> = | |
| { success: true, data: T, error: null } | |
| { success: false, data: null, error: AggregatedValidationError } | |
export class JsonSchema<T> { | |
private readonly validate | |
/** | |
* JSON Schemaを使ってパースしてくれるクラス。 | |
* | |
* NOTICE: ajvの`JSONSchemaType`は、 | |
* optionalなpropertyは必ずnullableだし、 | |
* nullableなものは必ずoptionalでもあるという仮定がある | |
* | |
* @see https://github.com/ajv-validator/ajv/issues/1375 | |
*/ | |
constructor(readonly schema: JSONSchemaType<T>, ajvInstance: Ajv = ajv) { | |
this.validate = ajvInstance.compile(schema) | |
} | |
private get lastError(): AggregatedValidationError { | |
return new AggregateError(this.validate.errors ?? [], `JSON did not match the schema: ${this.schema.$id ?? 'Unknown'}`) | |
} | |
/** | |
* @throws {AggregatedValidationError} | |
*/ | |
parse(data: unknown): T { | |
const valid = this.validate(data) | |
if (valid) { | |
return data | |
} else { | |
throw this.lastError | |
} | |
} | |
safeParse(data: unknown): SafeParseResult<T> { | |
const valid = this.validate(data) | |
if (valid) { | |
return { success: true, data, error: null } | |
} else { | |
return { success: false, data: null, error: this.lastError } | |
} | |
} | |
} | |
const SCHEMA = { | |
$id: 'User', | |
type: 'object', | |
properties: { | |
id: { type: 'number', nullable: true }, | |
name: { type: 'string' } | |
}, | |
required: ['name'], | |
additionalProperties: false | |
} as const | |
type User = FromSchema<typeof SCHEMA> | |
const User = new JsonSchema<User>(SCHEMA) | |
const user = User.parse({ id: 1, foo: 'John' }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment