npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-json-validator

v0.7.1

Published

Let JSON play nicely with Typescript

Downloads

66,587

Readme

ts-json-validator

codecov code style: prettier Build status npm version

Let JSON play nicely with Typescript.

⚠️ Warning: This project requires some features from Typescript 3.7, which was just released!

Type once, check all the time

example

Naturally, all of the code you write is typed perfectly. But you're not in charge of all that pesky data that comes from other places.

JSON.parse returns type any, which mangles all of your hard-earned strictness.

JSON validators are great, but they usually require you to define two things: the validation function and the Typescript type to go along with it. These can get out of sync and are generally a pain to maintain. JSON schema is a terrific idea, but the schemas are often tricky to write and even trickier to understand.

ts-json-validator allows you to define everything in one place. It generates a compliant JSON schema, a Typescript type that matches objects that can be parsed by that schema, and provides a typesafe parse that throws if the JSON you get doesn't match the type you're expecting.

This project uses ajv under the hood for fast JSON validation against a schema. If you don't want to validate every time, but still want a way to define a JSON schema that you can use as a Typescript type, that's OK too.

ts-json-validator exposes a pretty small API. It allows you to create and validate against a schema compliant with draft-07 of JSON Schema that also exposes a strict Typescript type expressing the types assignable to the schema. ts-json-validator implements all of draft-07.

Usage

First, import the important stuff:

import { createSchema as S, TsjsonParser, Validated } from "ts-json-validator"

Then define a schema. ts-json-validator currently supports every keyword, though not all of them contribute to the final derived type.

Let's say we want to define a schema that accepts objects with fields "a", "b", and "c". "a" is a required string, "b" is an optional number, and "c" is an optional string that can only take on the values "B1" or "B2".

// Make a parser that accepts objects with fields "a", "b", and "c"
const parser = new TsjsonParser(
  S({
    type: "object",
    properties: {
      a: S({ type: "string", title: "This is field A" }),
      b: S( {type: "number"} ),
      c: S({ type: "string", enum: ["B1", "B2"] as const })
    },
    required: ["a"] // possible fields autocomplete here
  })
);

You can see the generated schema:

JSON.stringify(parser.schema)
/*
{
  "type": "object",
  "properties": {
    "a": {
      "type": "string",
      "title": "This is field A"
    },
    "b": {
      "type": "number"
    },
    "c": {
      "type": "string",
      "enum": [
        "B1",
        "B2"
      ]
    }
  },
  "required": [
    "a"
  ]
}
*/

Or parse some string:

const stringToParse = JSON.stringify({ a: "Value for field A" });

const parsed = parser.parse(stringToParse);
/* parsed is of type
{
  a: string,
  b?: number,
  c?: "B1" | "B2"
}*/

console.log(parsed)
// { a: 'Value for field A' }

If you parse a string that doesn't match the schema and so can't be assigned to the expected type, it throws

const stringToParse = JSON.stringify({ a: "Value for field A", c: "Invalid" });

const parsed = parser.parse(stringToParse);
// throws
// Error: [{"keyword":"enum","dataPath":".c","schemaPath":"#/properties/c/enum","params":{"allowedValues":["B1","B2"]},"message":"should be equal to one of the allowed values"}]

You can skip validation, of course, but this is dangerous if you don't control the input:

const parsed = parser.parse(stringToParse, true);
// no validation; parsed might be the wrong type here.

If you just want to validate an object against the schema, but have no need to parse it, run

parser.validates(obj)

This is a type guard that returns true if obj can be validated by parser, otherwise false.

if (parser.validates(obj))
 // obj is the correct type in here
)
// obj is the unvalidated type out here

If you have a schema and want to use its type in helper functions, there is a helper type Validated<T> such that Validated<typeof schema> is the type defining all types that are assignable to this schema.

See the tests for more examples.

Goal

Ultimately, I hope that this can generate Typescript type/JSON schema pairs <T, s> such that

  1. Any type that s can validate is assignable to T
  2. As few types as possible that are assignable to T cannot be validated by s.
  • Step (1) is easily possible by assigning type T = any, but we want to narrow the type as far as possible to make this library actually useful.
  • Step (2) is generally impossible when validating against keywords that don't have a related type constraint (e.g. it's not really possible to have a type expressing all numbers between 0 and 1) but we can do a lot here for many keywords.

ENFORCED (💪) indicates that the field is enforced by the type system, and it should be impossible for any type assignable to T to fail JSON validation because of constraints that this field introduces.

For example, the required field on objects is ENFORCED because a type assignable to T is guaranteed to contain all fields marked required.

PARTIALLY ENFORCED (🔓) indicates that the field is partially enforced by the type system, but it may be possible to assign a type to T that fails validation against s.

For example, arrays with the additionalItems parameter are PARTIALLY ENFORCED becuase (currently) every element in the validated type can be assigned to the additionalItems type, when only items after items.length should be validated against this schema.

NOT ENFORCED (⚠️) indicates that the field is not enforced by the type system. This is either because it's impossible to do so efficiently given Typescript, or because I haven't figured out how yet. If the latter, hopefully I've included a comment.

For example, the pattern constraint in a string type is NOT ENFORCED because there's no reasonable way to express a type that means "a string that matches this regex".

NO ENFORCEMENT NEEDED (🤷) (means that this field does not add any constraints to a JSON schema so is essentially a comment.

NOT SUPPORTED (❌) means you can't currently define a TsjsonSchema that includes this validation keyword :(

| Keyword | Enforcement | Notes | |------|----|-----| | additionalProperties| 💪 | | | allOf| 💪 | | | anyOf| 💪 | | | const | 💪 | | | default | 💪 | Can only be assigned types that the rest of the schema validates | | else| 💪 | Only matters if if is supplied | | enum| 💪 | | | properties| 💪 | | | required | 💪 | | | then| 💪 | Only matters if if is supplied | | type| 💪 | Currently only supports single types (not lists of types) | | additionalItems| 🔓 | Enforced with some limitations | | items| 🔓 |Enforced if a schema, with limitations if a list of schemas | | oneOf| 🔓 | currently enforced as anyOf | | dependencies| 🔓 | Dependency schemas are not yet enforced. | | $comment| 🤷 | | | $id| 🤷 | | | $schema | 🤷 | | | contentEncoding| 🤷 | | | contentMediaType| 🤷 | | | description| 🤷 | | | examples| 🤷 | | | if| 🤷 | if doesn't actually add any constraints unless then or else are available | | readOnly| 🤷 | | | title| 🤷 | | | contains| ⚠️ |Still investigating enforcemnt | | exclusiveMaximum| ⚠️ | Can't enforce using type system | | exclusiveMinimum| ⚠️ | Can't enforce using type system | | format| ⚠️ | Can't enforce using type system | | maximum| ⚠️ | Can't enforce using type system | | maxItems| ⚠️ | Can't enforce using type system | | maxLength| ⚠️ | Can't enforce using type system | | maxProperties| ⚠️ | Can't enforce using type system | | minimum| ⚠️ | Can't enforce using type system | | minItems| ⚠️ | Can't enforce using type system | | minLength| ⚠️ | Can't enforce using type system | | minProperties| ⚠️ | Can't enforce using type system | | multipleOf| ⚠️ | Can't enforce using type system | | not| ⚠️ | Still investigating enforcement | | pattern| ⚠️ | Can't enforce using type system | | patternProperties| ⚠️ | Can't enforce using type system | | propertyNames| ⚠️ | Can't enforce using type system | | uniqueItems| ⚠️ | Can't enforce using type system | | $ref| ⚠️ | Still investigating enforcement. If you want a recursive type, you can use $ref, but the typescript type will not be strict. If you just want to refer to another type, don't use $ref – just assign a schema to a variable and use it in multiple places. | | definitions| ⚠️ | Still investigating enforcement along with $ref. |

See src/tsjson-parser.ts for more details, and the tests for interactive examples.

Installation

npm i ts-json-validator

How does all this work?

The object built up has the structure of a valid JSON schema with one extra magic feature: a hidden symbol that every schema uses to hold its own type.

Stability

The API is subject to change until 1.0, but the runtime behavior is very straightforward and is unlikely to be any more dangerous than ajv by itself. If you want to use this in a production-critical environment, I recommend pinning an exact release until 1.0 is out.

Contributing

Please do!