Skip to content

joanllenas/ts.data.json

Repository files navigation

ts.data.json

Build codecov npm version bundle size npm downloads

TypeScript types vanish at runtime, so the moment JSON crosses into your app from an API, a file, or localStorage, those compile-time guarantees are gone. ts.data.json puts them back: you describe the shape you expect with a decoder, and it validates the data at the boundary, handing you a fully typed value or a precise error.

All your JSON are belong to us

Features

  • Tiny & tree-shakeable -- zero dependencies, ships ESM + CJS, sideEffects: false. You bundle only the decoders you import.
  • Rich, structured errors -- every failure is a { message, path } issue, and all failures are reported at once, not just the first.
  • Standard Schema compliant -- decoders implement the Standard Schema spec, so they drop straight into any Standard-Schema-aware tool.
  • Type inference -- derive your static types from the decoders themselves with FromDecoder. No duplicate interfaces to keep in sync.

Installation

npm install ts.data.json

Quick example

One import gives you every decoder:

import * as JsonDecoder from 'ts.data.json';

Describe the shape you expect, then let TypeScript infer the type from it:

const userDecoder = JsonDecoder.object({
  id: JsonDecoder.number(),
  name: JsonDecoder.string(),
  roles: JsonDecoder.array(JsonDecoder.string()),
  lastLogin: JsonDecoder.nullable(JsonDecoder.string().map(iso => new Date(iso)))
});

// No separate interface needed:
type User = JsonDecoder.FromDecoder<typeof userDecoder>;
// { id: number; name: string; roles: string[]; lastLogin: Date | null }

Decode trusted-looking data and get back a typed value:

const result = userDecoder.decode({
  id: 123,
  name: 'Marty McFly',
  roles: ['user', 'premium'],
  lastLogin: '1985-10-26T01:21:00Z'
});

if (result.isOk()) {
  const user: User = result.value;
  console.log(`Welcome back, ${user.name}!`);
}

When the data is wrong, you get structured issues that point at exactly what failed:

const result = userDecoder.decode({
  id: 'not-a-number', // should be a number
  name: 'Marty McFly',
  roles: ['user', 42], // 42 should be a string
  lastLogin: null
});

if (!result.isOk()) {
  result.issues.forEach(issue => {
    console.log(`${JsonDecoder.formatIssuePath(issue.path)}: ${issue.message}`);
  });
  // id: "not-a-number" is not a valid number
  // roles[1]: 42 is not a valid string
}

Documentation

Full, auto-generated API docs and guides live on the documentation site:

New to JSON decoding? The introductory article Decoding JSON with TypeScript explains the how and why (slightly dated, but the ideas still hold).

Related libraries

License

Released under the BSD-3-Clause license.