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.
- 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.
npm install ts.data.jsonOne 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
}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).
Released under the BSD-3-Clause license.