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

decode-it

v1.2.11

Published

a simple zero-dependency type safe json decoder for typescript

Downloads

18

Readme

decode-it

Friendly json decoder with runtime type support

What the heck is a json decoder?

Json decoder guarantees that the json you've got matches the json you expect to get

Where can it be useful?

Whenever you have to deal with json comping from the input e.g when you're reading from a json file or fetching json data (yes, the everyday work you do)

How can I use it?

You specify an schema (or you might call it an interface) And it will throw as soon as it finds out that the schema doesn't match the received json

Why do I need this? I use typescript, I can already do that without this with the magic of interfaces

Well... Not exactly, you might think doing something that will result in the same functionally as what json decoder is trying to do, but no, let me demonstrate it for you

interface Data {
  name: string;
  gender: 'female' | 'male';
}

fetch('https://example.com')
  .then(res => res.json())
  .then((data: Data) => console.log(data));

But what happens if I try to mess up the interface like so that the name becomes number with no gender field

interface Data {
  name: number;
}

If the json is the same as the previous Data interface (with name being string) it won't throw an error You can try it, you'll see that in the console.log that the object you got differs from your interface and you've got no errors Why you ask? Because all this type checking only happens in compile time meaning these types are all just predictions And when do we get the json? At runtime (where no typescript type exists) so you need some kind of "typescript interface like" support in runtime, and that's where json decoders come in Similar to how you define an interface for an object in typescript You define an schema for a json (object) and it will throw some helpful error messages as soon as it finds a mismatch and the good part is that all of this happens at runtime!

Usage

createDecoder:

createDecoder is a higher order function which accepts a valid schema as the input and then returns a function which accepts a json as the input and returns the json if it matched the given schema or throws if it found a mismatch e.g:

import { createDecoder } from 'decode-it';
const decode = createDecoder(schema);
const data = decode(susData); // if the schema didn't match the susData it will throw

schema:

schema can be either an object which include validators or just an array validator (in case of array jsons)

import { V } from 'decode-it';

const schema = {
  name: V.string(), // 🟢 "Mohammad" 🔴 536574
  hobbies: V.array(V.string()), // 🟢 ["programming", "running", "sleeping"] 🔴 ["programming", 12312341234 ]
};

validators:

a validator is function which is called on a field and asserts that the type matches the validator there are two types of validators one which take no input, you can call them primitive validator for now which include:

V.string: a validator which asserts this value is of type string

import { V } from 'decode-it';

const schema = {
  firstName: V.string(), //🟢 "Ali" 🔴 null
  lastName: V.string(), // 🟢 "string" 🔴 true
};

V.number: a validator which asserts this value is of type number

import { V } from 'decode-it';

const schema = {
  age: V.number(), //🟢 17 🔴 "17"
  weight: V.number(), // 🟢 63 🔴 { kg: 63 }
};

V.boolean: a validator which asserts this value is of type boolean

import { V } from 'decode-it';

const schema = {
  isAdult: V.boolean(), //🟢 false 🔴 "yes"
  isMale: V.boolean(), // 🟢 true 🔴 ["prefer not to say"]
};

V.nil: a validator which asserts this value is of type null

import { V } from 'decode-it';

const schema = {
  yeahNullSucks: V.nil(), //🟢 null 🔴 "no null is the best"
  nilIsTheSameAsNull: V.nil(), // 🟢 null 🔴 undefined
};

the second group take (or can take) an input or more, you can call them custom validators:

V.literal: a validator which takes any value and asserts that the input is exactly equal as this value (note: a deep comparison is done here) e.g:

import { V } from 'decode-it';

const schema = {
  name: V.literal('Alfred'), // 🟢 "Alfred" 🔴 "alfred"
  state: V.literal({ happy: true }), // 🟢 { happy: true } 🔴 { happy: false }
};

V.array: a validator which takes another validator and asserts all validators pass for each element of this array value e.g:

import { V } from 'decode-it';

const schema = {
  players: V.array(V.string()), // 🟢 [] 🟢 ["lazy-bones", "ass-kicker", "player256346"] 🔴 [1, 3, 4]
  friendsSkills: V.array({ name: V.string(), id: V.number() }), // 🟢 [] 🟢 [{ name: "gaming", id: 1574234 }] 🔴 [{ name: "gaming", id: "1574234" }]
  uselessField: V.array(V.array(V.nil())), // 🟢 [] 🟢 [[null, null]] 🔴 [null,null]
};

V.union: a validator which takes any number of validators (starting from two) and asserts if this value passes any of the given validators

(note: this validator doesn't support scheme (object) types as validators and it's better to avoid using this validator as much as you can because this type is rarely useful when it comes to jsons) e.g:

import { V } from 'decode-it';

const schema = {
  countryArea: V.union(V.string(), V.number()), // 🟢 1648195 🟢 "1.648 million km^2" 🔴 null
  cities: V.union(
    V.array({ name: V.string(), visited: V.boolean() }),
    V.array(V.string()),
  ), // 🟢 [{ name: "Tehran", visited: true }] 🟢 ["Tehran"] 🔴 false
};

V.optional: a validator which takes one validator and asserts this value can be undefined or it should pass the validator

import { V } from 'decode-it';

const schema = {
  favoriteLang: V.optional(V.string()), // 🟢 undefined 🟢 "typescript" 🔴 null
  favoriteLangCapabilities: V.optional({
    isFunctional: V.boolean(),
    typeSystem: V.union(V.literal('static'), V.literal('dynamic')),
  }), // 🟢 undefined 🟢 { isFunctional: true, typeSystem: "static" } 🔴  { isFunctional: 'no', typeSystem: "static" }
};

V.tuple: a validator which takes any number of validators and asserts if the given value is tuple of given validators

import { V } from 'decode-it';

const schema = {
  tick: V.tuple(V.string(), V.number()), // 🟢 ['10/2/1991', 1000] 🔴 ['10/2/1991', 1000, 2000] 🔴 ['10/2/1991'] 🔴 []
  verboseTick: V.tuple({ from: V.array(V.string()) }, { ms: V.array(V.number()) }), // 🟢 [{ from: ['10/2/1991', '9/7/2007'] }, { ms: [1000, 300] }] 🔴 [{ from: ['10/2/1991', '9/7/2007'] }, { ms: [1000, 'haha'] }]
};

toNativeType:

say you've came this far and created an schema e.g:

const userSchema = {
  name: { firstName: V.string(), lastName: V.string() },
  age: V.number(),
  isAdult: V.boolean(),
  birthDate: V.tuple(V.number(), V.number(), V.number()),
  pet: V.optional(V.union(V.literal('cat'), V.literal('dog'), V.literal('chick'))),
};

const usersSchema = V.array(userSchema);

ok so you'd also need an typescript interface too right? of course you do! well you can just write it yourself lazy bones, just convert each validator to it's own typescript type, you can do a simple task as that right? problem solved :))

you're not gonna do that? that's completely fine! we programmers hate overdoing or more specifically repeating ourselves we like to keep things DRY as possible

introducing toNativeType type constructor it does exactly what you'd expect, magically converting your schema to the appropriate typescript type. how to use it? simple:

const userSchema = {
  name: { firstName: V.string(), lastName: V.string() },
  age: V.number(),
  isAdult: V.boolean(),
  birthDate: V.tuple(V.number(), V.number(), V.number()),
  pet: V.optional(V.union(V.literal('cat'), V.literal('dog'), V.literal('chick'))),
};

const usersSchema = V.array(userSchema);

type User = toNativeType<typeof userSchema>;
type Users = toNativeType<typeof usersSchema>; // or just simply User[]

boom, now you cover both runtime and compile type, with only one schema.

oh and if you don't trust this api or package in whole, this library is completely tested you can take look at them in the source code. and guess what? the examples used here (validator examples) are also tested, just check the test/examples.spec.ts even tough, if you found any bugs or issues, I'd appreciate it