1.1.8 • Published 7 months ago

@imhonglu/json-schema v1.1.8

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

@imhonglu/json-schema

English | 한국어

Introduction

  • A library that complies with JSON Schema 2020-12-draft specification
  • Validated based on JSON-Schema-test-suite
  • Supports static type inference based on schema definitions

demo-1

Table of Contents

Features

  • TypeScript static type checking support for schema definitions
  • Smart type inference
    • Required/optional field inference based on required property
    • Type inference for fields with default values / function type support
    • Recursive type inference for nested objects
  • Class-based schema definition support
  • Custom features
    • Schema validation message customization
    • Custom error handling
    • Validation rule extension support

Implementation Status

Currently, 1,487 out of 1,563 test cases have passed (95.1%)

Excluded items:

  • defs: Schema definition related tests, excluded as they are not related to validation
  • format: Excluded as it belongs to the optional category

Unsupported items:

  • anchor
  • refRemote
  • vocabulary
  • dynamicRef
  • optional/anchor
  • optional/dynamicRef
  • optional/dependencies-compatibility
  • optional/format/relative-json-pointer
  • optional/format/json-pointer

Installation

npm install @imhonglu/json-schema

Usage

Basic Schema Definition

// address.ts
import { createSchemaClass } from "@imhonglu/json-schema";

export class Address extends createSchemaClass({
  type: "object",
  properties: {
    street: { type: "string" },
    city: { type: "string" },
    zip: { type: "string" },
  },
  // Define street property as required
  required: ["street"],
}) {}

// ✅ Type inference result:
// {
//   street: string;   // required
//   city?: string;    // optional
//   zip?: string;     // optional
// }

Nested Schema

// person.ts
import { createSchemaClass } from "@imhonglu/json-schema";
import { Address } from "./address.js";

export class Person extends createSchemaClass({
  type: "object",
  properties: {
    name: { type: "string" },
    address: Address, // Nested Address schema
    createdAt: {
      type: "string",
      default: () => new Date().toISOString(),
    },
    // String field that allows null
    deletedAt: {
      type: ["string", "null"],
      default: null,
    },
  },
  required: ["name", "createdAt", "deletedAt"],
}) {}

// ✅ Type inference result:
// {
//   name: string;                // required
//   address?: Address;           // optional
//   createdAt: string;           // required
//   deletedAt: string | null;    // required
// }

Usage with new

import { Person } from "./person.js";

// Create instance with object literal
const johnDoe = new Person({
  name: "John Doe",
  address: {
    street: "123 Main St",
    city: "Toronto",
    zip: "M5H 2N2",
  },
});

// ✅ Result:
// {
//   name: 'John Doe',
//   address: Address {
//     street: '123 Main St',
//     city: 'Toronto',
//     zip: 'M5H 2N2'
//   },
//   deletedAt: null
// }

// ✅ Property access
console.log(johnDoe.name);            // 'John Doe'
console.log(johnDoe.address?.street); // '123 Main St'
console.log(johnDoe.deletedAt);       // null

// ✅ Using directly created Address instance
const maryDoe = new Person({
  name: "Mary Doe",
  address: new Address({
    street: "456 Main St",
    city: "Toronto",
    zip: "M5H 2N2",
  }),
});

Parsing with parse

The parse method creates a schema instance by parsing a string.

const person = Person.parse('{ "name": "John" }'); // Person

Parsing with safeParse

You can safely parse using the safeParse method.

const person = Person.safeParse('{ "name": "John" }'); // SafeResult<Person>

if (person.success) {
  console.log(person.data); // Person
} else {
  console.error(person.error); // ValidationFailedError
}

Serializing with JSON.stringify

The toJSON method is implemented, allowing serialization through JSON.stringify.

const person = new Person({ name: "John" });
const json = JSON.stringify(person); // '{"name":"John","deletedAt":null}'

Known Issues

Type Inference Limitations for Inline Schemas

There is an issue where type inference for the required property doesn't work properly for inline-defined sub-schemas.

For example:

class Person extends createSchemaClass({
  type: "object",
  properties: {
    name: { type: "string" },
    address: {
      type: "object",
      properties: {
        street: { type: "string" },
        city: { type: "string" },
        zip: { type: "string" },
      },
      required: ["street"], // This constraint is not properly applied
    },
  },
  required: ["name"],
}) {}

// ❌ No type error due to incorrect inference
const person = new Person({
  name: "John Doe",
  address: {}, // No type error despite street being required
});

Solution: Separate into Independent Classes

To resolve this issue, sub-schemas should be defined as separate classes.

// ✅ Separate into independent class
class Address extends createSchemaClass({
  type: "object",
  properties: {
    street: { type: "string" },
    city: { type: "string" },
    zip: { type: "string" },
  },
  required: ["street"],
}) {}

class Person extends createSchemaClass({
  type: "object",
  properties: {
    name: { type: "string" },
    address: Address, // Reference Address class
  },
  required: ["name"],
}) {}

const person = new Person({
  name: "John Doe",
  address: {}, // ✅ Type error: street property is required
});

API Reference

Core API

JSON Schema Specification Types

1.1.8

7 months ago

1.1.7

8 months ago

1.1.6

8 months ago

1.1.5

8 months ago

1.1.4

8 months ago

1.1.3

8 months ago

1.1.2

8 months ago

1.1.1

9 months ago

1.1.0

9 months ago

1.0.8

9 months ago

1.0.7

9 months ago

1.0.6

9 months ago

1.0.5

9 months ago

1.0.4

9 months ago

1.0.3

10 months ago

1.0.2

10 months ago

1.0.1

10 months ago

1.0.0

10 months ago