@topgunbuild/typed
TypeScript icon, indicating that this package has built-in type declarations

0.1.0 • Public • Published

✅ @topgunbuild/typed

Fast, tiny and type-safe runtime validation library for TopGun

npm bundlephobia License: MIT

Install

npm install @topgunbuild/typed

Usage

import * as t from "@topgunbuild/typed";

const post = t.object({
  id: t.number(),
  title: t.string(),
});

const postList = t.array(post);

// Get the actual type of the postList
type PostList = t.Infer<typeof postList>;

// Some json data from somewhere
const data = {} as any;

const result = postList(data);

if (result.ok) {
  // Do something with the data
  result.data;
} else {
  // Handle the error
  result.error;
}

// Or you can just unwrap the value directly. It will throw if the data is invalid.
const parsed = t.unwrap(result);

// Or if you don't want it to throw, you can use `unwrapOr`
const parsed = t.unwrapOr(result, {
  /* a default value */
});

Fetch example

Validate data from a remote API.

import * as t from "@topgunbuild/typed";

const post = t.object({
  id: t.number(),
  title: t.string(),
});

const postList = t.array(post);

// If everything goes ok, posts will be correctly typed as `Post[]`.
// If not, an error will be thrown.
const posts = await fetch("https://jsonplaceholder.typicode.com/posts")
  .then((res) => res.json())
  .then(postList)
  .then(t.unwrap);

Custom Types

There's a chance you'll want to define more complex types to deal with your data. You can do this in a few ways:

  • Using the map function.
  • Using the chain function.
  • Creating a struct from scratch.

Using the map function

The map function allows you to convert one "base" type into another. It always starts from a base type.

import * as t from "@topgunbuild/typed";

// Suppose we have this geolocation struct.
const latLng = t.object({
  lat: t.number(),
  lng: t.number(),
});

// `asNumber` means we can pass a string and it will be converted to a number.
const latLngPair = t.tuple([t.asNumber(), t.asNumber()]);

// And we'd like to have a type that takes a string a returns a `LatLng`.
const asLatLng = t.map(t.string(), (str) => {
  // Here `str` is guaranteed to be a string.

  // Here we validate our splited string against a tuple of two numbers.
  const result = latLngPair(str.split(","));

  // If it succeeds we return a `LatLng` struct. If not, forwards the error.
  return t.isOk(result) ? latLng(result.data) : result;
});

// Now we can use `asLatLng` to validate a string.
const str = "42.123,42.123";

const result = asLatLng(str); // `result` will be a `LatLng` struct.

Using the chain function

The chain function is useful when you don't want to change the type of your data, but further process it.

For example, if you have a string that you want to trim and lowercase it, then chain is the function you want to use.

import * as t from "@topgunbuild/typed";

const trim = (value: string) => value.trim();
const lower = (value: string) => value.toLowerCase();

const trimLower = t.chain(
  t.string(),
  trim,
  lower /* whatever else function you want as longs as it at takes the same type and returns the same type */,
);

const result = trimLower("  Hello World  "); // { ok: true, value: "hello world" }

Creating a struct from scratch

A struct is nothing more than a function that takes whatever input and returns a Result. The convention in @topgunbuild/typed is to have factory functions that return a struct just to be able to customize error messages. This was not the case in previous versions of typed, but it is now.

import * as t from "@topgunbuild/typed";

const regex =
  (regex: RegExp, msg = "Expecting value to match regex"): t.Struct<string> =>
  (input) => {
    if (typeof input !== "string" || !regex.test(input)) {
      return t.err(new t.StructError(msg, { input }));
    }
    return t.ok(input);
  };

You can browse the @topgunbuild/typed source code to see how structs are implemented if you're curious.

Notes

@topgunbuild/typed will deep clone non primitive values as it validates them. So if you pass an object or array to a struct, it will be cloned. This is to say that @topgunbuild/typed will get rid of any extra properties on your data, so it'll exactly match the shape you defined.

Reference

The code in this repository is based on typed (https://github.com/brielov/typed).

License

MIT

Package Sidebar

Install

npm i @topgunbuild/typed

Weekly Downloads

2

Version

0.1.0

License

MIT

Unpacked Size

31.1 kB

Total Files

7

Last publish

Collaborators

  • ivkan