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

io-ts-promise

v2.0.2

Published

io-ts for developers who like Promises

Downloads

38,120

Readme

io-ts-promise

While io-ts is a great library, it can be a bit alienating unless you are familiar with functional programming. So if you just want to ensure the runtime types for the data fetched from your API, you might be looking for something simpler. This is where io-ts-promise tries to help out.

It provides the following:

Usage

Promise chain decoding

Decode data from Promise based APIs, without having to worry about how to retrieve data from the Either values returned by the io-ts types.

import * as t from 'io-ts';
import * as tPromise from 'io-ts-promise';

const Person = t.type({
  name: t.string,
  age: t.number,
});

fetch('http://example.com/api/person')
  .then(response => tPromise.decode(Person, response.json()))
  .then(typeSafeData =>
    console.log(`${typeSafeData.name} is ${typeSafeData.age} years old`),
  );

tPromise.decode also supports currying, so we can simplify the code with

fetch('http://example.com/api/person')
  .then(response => response.json())
  .then(tPromise.decode(Person))
  .then(typeSafeData =>
    console.log(`${typeSafeData.name} is ${typeSafeData.age} years old`),
  );

As with any Promise-based API, you can also use tPromise.decode in async code as following

const response = await fetch('http://example.com/api/person');
const typeSafeData = await tPromise.decode(Person, response.json());
console.log(`${typeSafeData.name} is ${typeSafeData.age} years old`);

Identifying errors

When building long promise chains, you might handle errors somewhere else than directly next to the function producing the error. In these cases you might want to identify the errors in order to act accordingly. Errors produced by the decode function due to incompatible data can be identified by either using the type guard tPromise.isDecodeError(error), or checking the error type with error instanceof tPromise.DecodeError. For example:

fetch('http://example.com/api/not-a-person')
  .then(response => response.json())
  .then(tPromise.decode(Person))
  .then(typeSafeData =>
    console.log(`${typeSafeData.name} is ${typeSafeData.age} years old`),
  )
  .catch(error => {
    if (tPromise.isDecodeError(error)) {
      console.error('Request failed due to invalid data.');
    } else {
      console.error('Request failed due to network issues.');
    }
  });

Creating custom types

Writing custom io-ts types is a bit cryptic, so this library provides a simpler way of extending existing io-ts types, or creating your own from scratch. All you need is a function which decodes incoming values, and another which encodes it back.

import * as t from 'io-ts';
import * as tPromise from 'io-ts-promise';

// New type extending from existing type
const Price = tPromise.extendType(
  t.number,
  // Decode function takes in number and produces wanted type
  (value: number) => ({
    currency: 'EUR',
    amount: value,
  }),
  // Encode function does the reverse
  price => price.amount,
);

// And use them as part of other types
const Product = t.type({
  name: t.string,
  price: Price,
});

fetch('http://example.com/api/product')
  .then(response => response.json())
  .then(tPromise.decode(Product))
  .then(typeSafeData =>
    console.log(
      `${typeSafeData.name} costs ${typeSafeData.price.amount} ${
        typeSafeData.price.currency
      }`,
    ),
  );

Alternatively, you can define the type from scratch, in which case the decoder will receive a value of unknown type to decode into desired runtime type.

// Custom type from scratch
const Price = tPromise.createType(
  // Decode function takes in unknown and produces wanted type
  (value: unknown) => {
    if (typeof value === 'number') {
      return {
        currency: 'EUR',
        amount: value,
      };
    } else {
      throw new Error('Input is not a number');
    }
  },
  // Encode function does the reverse
  price => price.amount,
);

Decoders

In case you only need to read data into your application, you can use decoders which only convert data in one way.

Note: io-ts doesn't support decoders in its nested types such as t.array or t.type. So only use decoders for top level data structures. The decode function of this library suppports both types and decoders.

The easiest way is to create a decoder is to extend and existing io-ts type, and only perform the desired additional modification on top of that.

import * as tPromise from 'io-ts-promise';

const Person = t.type({
  name: t.string,
  age: t.number,
});

const ExplicitPerson = tPromise.extendDecoder(Person, person => ({
  firstName: person.name,
  ageInYears: person.age,
}));

fetch('http://example.com/api/person')
  .then(response => tPromise.decode(ExplicitPerson, response.json()))
  .then(typeSafeData =>
    console.log(
      `${typeSafeData.firstName} is ${typeSafeData.ageInYears} years old`,
    ),
  );

You can also create decoders from scratch using createDecoder, but I have yet to find a good example of where this would be convenient.