SkillAgentSearch skills...

Doubter

🤔 Runtime validation and transformation library.

Install / Use

/learn @smikhalevski/Doubter
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <a href="#readme"><picture> <source media="(prefers-color-scheme: dark)" srcset="./assets/logo-dark.png" /> <source media="(prefers-color-scheme: light)" srcset="./assets/logo-light.png" /> <img alt="Doubter" src="./assets/logo-light.png" width="400" /> </picture></a> </p> <p align="center"> <a href="https://codesandbox.io/s/y5kec4"><img src="./assets/button-playground.png" alt="Playground" height="41"/></a> <a href="https://smikhalevski.github.io/doubter/modules/core.html"><picture> <source media="(prefers-color-scheme: dark)" srcset="./assets/button-api-docs-dark.png" /> <source media="(prefers-color-scheme: light)" srcset="./assets/button-api-docs-light.png" /> <img alt="API Docs" src="./assets/button-api-docs-light.png" height="41" /> </picture></a> </p> <br> <!--ARTICLE--> <!--OVERVIEW-->

Runtime validation and transformation library.

<!--/OVERVIEW--> <br>
npm install --save-prod doubter
<br> <!--/ARTICLE--> <!--TOC-->

<span class="toc-icon">🔰 </span>Features

<span class="toc-icon">⏱ </span>Performance

<span class="toc-icon">🍿 </span>Comparison with peers

<span class="toc-icon">🧩 </span>Data types

<span class="toc-icon">🍪 </span>Cookbook

<!--/TOC--> <!--ARTICLE-->

Introduction

Let's create a simple shape of a user:

import * as d from 'doubter';

const userShape = d.object({
  name: d.string(),
  age: d.number(),
});
// ⮕ Shape<{ name: string, age: number }>

This is the shape of an object with two required properties "name" and "age". Shapes are the core concept in Doubter, they are validation and transformation pipelines that have an input and an output.

Apply the shape to an input value with the parse method:

userShape.parse({
  name: 'John Belushi',
  age: 30,
});
// ⮕ { name: 'John Belushi', age: 30 }

If the provided value is valid, then it is returned as is. If an incorrect value is provided, then a validation error is thrown:

userShape.parse({
  name: 'Peter Parker',
  age: 'seventeen',
});
// ❌ ValidationError: type.number at /age: Must be a number

Currently, the only constraint applied to the "age" property value is that it must be a number. Let's modify the shape to check that age is an integer and that user is an adult:

  const userShape = d.object({
    name: d.string(),
-   age: d.number()
+   age: d.number().int().between(18, 100)
  });

Here we added two operations to the number shape. Operations can check, refine, and alter input values. There are lots of operations available through plugins, and you can easily add your own operation when you need a custom logic.

Now shape would not only check that the "age" is a number, but also assert that it is an integer between 18 and 100:

userShape.parse({
  name: 'Peter Parker',
  age: 16,
});
// ❌ ValidationError: number.gte at /age: Must be greater than or equal to 18

If you are using TypeScript, you can infer the type of the value that the shape describes:

type User = d.Input<typeof userShape>;

const user: User = {
  name: 'Dan Aykroyd',
  age: 27,
};

Read more about static type inference and runtime type introspection.

Async shapes

Most of the shapes are synchronous, but they may become asynchronous when one of the below is used:

Let's have a look at a shape that synchronously checks that an input value is a string:

const shape1 = d.string();
// ⮕ Shape<string>

shape1.isAsync; // ⮕ false

If we add an async operation to the string shape, it would become asynchronous:

const shape2 = d.string().checkAsync(value => doAsyncCheck(value));
// ⮕ Shape<string>

shape2.isAsync; // ⮕ true

The shape that checks that the input value is a Promise instance is synchronous, because it doesn't have to wait for the input promise to be fulfilled before ensuring that input has a proper type:

const shape3 = d.promise();
// ⮕ Shape<Promise<any>>

shape3.isAsync; // ⮕ false

But if you want to check that a promise is fulfilled with a number, here when the shape becomes asynchronous:

const shape4 = d.promise(d.number());
// ⮕ Shape<Promise<number>>

shape4.isAsync; // ⮕ true

Asynchronous shapes don't support synchronous parsing, and would throw an error if it is used:

shape4.parse(Promise.resolve(42));
// ❌ Error: Shape is async

shape4.parseAsync(Promise.resolve(42));
// ⮕ Promise { 42 }

On the other hand, synchronous shapes support asynchronous parsing:

d.string().parseAsync('Mars');
// ⮕ Promise { 'Mars' }

The shape that depends on an asynchronous shape, also becomes asynchronous:

const userShape = d.object({
  avatar: d.promise(d.instanceOf(Blob)),
});
// ⮕ Shape<{ avatar: Promise<Blob> }>

userShape.isAsync; // ⮕ true

Parsing and trying

All shapes can parse input values and there are several methods for that purpose. Consider a number shape:

const shape1 = d.number();
// ⮕ Shape<number>

The parse method takes an input value and returns an output value, or throws a validation error if parsing fails:

shape.parse(42);
// ⮕ 42

shape.parse('Mars');
// ❌ ValidationError: type.number at /: Must be a number

It isn't always convenient to write a try-catch blocks to handle validation errors. Use the try method in such cases:

shape.try(42);
// ⮕ { ok: true, value: 42 }

shape.try('Mars');
// ⮕ { ok: false, issues: [ … ] }

Read more about issues in Validation errors section.

Sometimes you don't care about validation errors, and want a default value to be returned if things go south. Use the parseOrDefault method for that:

shape.parseOrDefault(42);
// ⮕ 42

shape.parseOrDefault('Mars');
// ⮕ undefined

shape.parseOrDefault('Pluto', 5.3361);
// ⮕ 5.3361

If you need a fallback value for a nested shape consider using the catch method.

For asynchronous shapes there's an alternative for each of those methods: parseAsync, tryAsync, and parseOrDefaultAsync.

Methods listed in this section can be safely detached from the shape instance:

const { parseOrDefault } = d.string();

parseOrDefault('Jill');
// 
View on GitHub
GitHub Stars69
CategoryDevelopment
Updated3d ago
Forks2

Languages

TypeScript

Security Score

100/100

Audited on Mar 31, 2026

No findings