Skip to content

GuccioGucci/contented

Repository files navigation

A library to coerce values at run-time.

NPM Test Bundlephobia Minified

Table of Contents

Introduction

Contented is a TypeScript library for performing type coercion at run-time. To this end, Contented introduces run-time representations of primitive types, such as string, which can be then mixed and matched to describe compound types.

import { string, number, object } from '@gucciogucci/contented';

const Image = object({
  url: string,
  size: number
});

Contented may be useful every time there are expectations — but no real guarantees, on the shape of data acquired at run-time. Common use cases include processing data coming over the wire, from files, or any other external source.

Reference

Coercing

isValid(T, input)

A type-guard that returns true if input is evaluated to be a T, and false otherwise.

import { number, object, isValid } from '@gucciogucci/contented';

const Point = object({
  x: number,
  y: number
});

if (isValid(Point, input)) {
  // here input: { x: number, y: number }
}

explain(T, input)

Explains why input cannot be coerced to T. It returns undefined if no explanation is needed, that is, if input is in fact a T.

import { number, object, explain } from '@gucciogucci/contented';

const Point = object({
  x: number,
  y: number
});

explain(Point, { x: 10 });
/* {
     value: { x: 10 },
     isNot: { object: { x: 'number', y: 'number' } },
     since: [ { missingKey: 'y' } ]
   }
*/

explain(Point, { x: 'hello', y: 'there' })
/* {
     value: { x: 'hello', y: 'there' },
     isNot: { object: { x: 'number', y: 'number' } },
     since: [
       { atKey: 'x', value: 'hello', isNot: 'number' },
       { atKey: 'y', value: 'there', isNot: 'number' }
     ]
   }
*/

explain(Point, { x: 10, y : 20 });
// undefined

Primitive types

string

A run-time representation of the string type.

import { string, isValid, explain } from '@gucciogucci/contented';

isValid(string, 'hello');
// true

explain(string, 42);
// { value: 42, isNot: 'string' }

number

A run-time representation of the number type.

import { number, isValid, explain } from '@gucciogucci/contented';

isValid(number, 42);
// true

explain(number, 'hello');
// { value: 'hello', isNot: 'number' }

bigint

A run-time representation of the bigint type.

import { bigint, isValid, explain } from '@gucciogucci/contented';

isValid(bigint, 1024n);
// true

explain(bigint, 'hello');
// { value: 'hello', isNot: 'bigint' }

boolean

A run-time representation of the boolean type.

import { boolean, isValid, explain } from '@gucciogucci/contented';

isValid(boolean, false);
// true

explain(boolean, 'hello');
// { value: 'hello', isNot: 'boolean' }

null_

A run-time representation of the null type. The trailing underscore is to avoid shadowing the built-in null value.

import { null_, isValid, explain } from '@gucciogucci/contented';

isValid(null_, null);
// true

explain(null_, 'hello');
// { value: 'hello', isNot: 'null' }

Literal types

literal

A run-time representation of the narrowest type that can be constructed from value. Hence, coercions to literal(value) succeed only when value is provided as an input.

import { literal, isValid, explain } from '@gucciogucci/contented';

isValid(literal('hello'), 'hello');
// true

explain(literal('hello'), 'foo');
// { value: 'foo', isNot: { literal: 'hello' }  }

Compound types

object

A run-time representation of an object.

import { number, object, isValid, explain } from '@gucciogucci/contented';

const Point = object({ x: number, y: number });

isValid(Point, { x: 10, y : 20 });
// true

explain(Point, { x: 10 });
/* {
     value: { x: 10 },
     isNot: { object: { x: 'number', y: 'number' } },
     since: [ { missingKey: 'y' } ]
   }
*/

As with compile-time types, optional properties are marked by adding a ? at the end of their names:

import { number, object, isValid } from '@gucciogucci/contented';

const Point = object({ x: number, y: number, 'z?': number })

isValid(Point, { x: 10, y: 20 });
// true

isValid(Point, { x: 10, y: 20, z: 30 });
// true

isValid(Point, { x: 10, y: 20, z: undefined });
// true

arrayOf(T)

A run-time representation of an array of Ts, where T denotes the run-time representation of its element type.

import { number, arrayOf, isValid, explain } from '@gucciogucci/contented';

isValid(arrayOf(number), [ 3, 4, 5 ]);
// true

explain(arrayOf(number), 'hello');
// { value: 'hello', isNot: { arrayOf: 'number' } }

explain(arrayOf(number), [ 3, 'a', 5 ]);
/* {
     value: [ 3, 'a', 5 ],
     isNot: { arrayOf: 'number' },
     since: [ { atKey: 1, value: 'a', isNot: 'number' } ]
   }
*/

oneOf(T1, T2, ...Ts)

A run-time representation of the union type T1 | T2 | ...Ts.

import { oneOf, literal, isValid, explain } from '@gucciogucci/contented';

const abc = oneOf(literal('a'), literal('b'), literal('c'));

isValid(abc, 'a');
// true

explain(abc, 'd');
/* {
     value: 'd',
     isNot: { oneOf: [ { literal: 'a' }, { literal: 'b' }, { literal: 'c' } ] },
     since: [
       { value: 'd', isNot: { literal: 'a' } },
       { value: 'd', isNot: { literal: 'b' } },
       { value: 'd', isNot: { literal: 'c' } }
     ]
   }
*/

allOf(T1, T2, ...Ts)

A run-time representation of the intersection type T1 & T2 & ...Ts.

import { allOf, object, number, isValid, explain } from '@gucciogucci/contented';

const abObject = allOf(object({ a: number }), object({ b: number }));

isValid(abObject, { a: 10, b: 20 });
// true

explain(abObject, { a: 10 });
/* {
     value: { a: 10 },
     isNot: { allOf: [ { object: { a: 'number' } }, { object: { b: 'number' } } ] },
     since: [{
       value: { a: 10 },
       isNot: { object: { b: 'number' } },
       since: [ { missingKey: 'b' } ]
     }]
   }
*/

Utility types

Infer

Infer comes in handy every time it is necessary to infer the compile-time type corresponding to some run-time representation T.

import { Infer, string, object } from '@gucciogucci/contented';

const User = object({
  name: string,
  surname: string,
  contacts: object({ phone: string })
});

function fn(user: Infer<typeof User>) {
  // here, user: { name: string; surname: string; contacts: { phone: string } }
}

License

Copyright 2024 Gucci.

Licensed under the GNU Lesser General Public License, Version 3.0