UNPKG

tiny-types

Version:

A tiny library that brings Tiny Types to JavaScript and TypeScript

140 lines (132 loc) 4.21 kB
import { ensure } from './ensure'; import { equal, significantFieldsOf, stringify, toJSON } from './objects'; import { isDefined } from './predicates'; import { JSONValue, Serialisable } from './types'; /** * @desc The {@link TinyTypeOf} can be used to define simple * single-value {@link TinyType}s on a single line. * * It contains a check preventing the constructor argument from being undefined (see {@link isDefined}); * * @experimental * * @example * class Username extends TinyTypeOf<string>() {} * * @example * class Age extends TinyTypeOf<number>() {} * * @returns a dynamically created base class your tiny type can extend from */ export function TinyTypeOf<T>(): new(_: T) => { value: T } & TinyType { return class extends TinyType { constructor(public readonly value: T) { super(); ensure(this.constructor.name, value, isDefined()); } }; } /** * @desc The {@link TinyType} abstract class should be used as a base class for your own Tiny Types. * * If you want the Tiny Type to wrap a single value use the {@link TinyTypeOf} instead as it will save you some keystrokes. * * @example * class FirstName extends TinyTypeOf<string>() {} * class LastName extends TinyTypeOf<string>() {} * class Age extends TinyTypeOf<number>() {} * * class Person extends TinyType { * constructor(public readonly firstName: FirstName, * public readonly lastName: LastName, * public readonly age: Age, * ) { * super(); * } * } */ export abstract class TinyType implements Serialisable { /** * @desc Compares two tiny types by value * * @example <caption>Comparing simple types</caption> * class Id extends TinyTypeOf<string>() {} * * const id = new Id(`3cc0852d-fda7-4f61-874e-0cfadbd6182a`); * * id.equals(id) === true * * @example <caption>Comparing complex types recursively</caption> * class FirstName extends TinyTypeOf<string>() {} * class LastName extends TinyTypeOf<string>() {} * class Age extends TinyTypeOf<number>() {} * * class Person extends TinyType { * constructor(public readonly firstName: FirstName, * public readonly lastName: LastName, * public readonly age: Age, * ) { * super(); * } * } * * const p1 = new Person(new FirstName('John'), new LastName('Smith'), new Age(42)), * p2 = new Person(new FirstName('John'), new LastName('Smith'), new Age(42)); * * p1.equals(p2) === true * * @param {TinyType} another * @returns {boolean} */ equals(another: TinyType): boolean { return equal(this, another); } /** * @desc Serialises the object to its string representation * * @returns {string} */ toString(): string { return stringify(this); } /** * @desc Serialises the object to a JSON representation. * * @example * class FirstName extends TinyTypeOf<string>() {} * * const name = new FirstName('Jan'); * * name.toJSON() === 'Jan' * * @example * class FirstName extends TinyTypeOf<string>() {} * class LastName extends TinyTypeOf<string>() {} * class Age extends TinyTypeOf<number>() {} * * class Person extends TinyType { * constructor(public readonly firstName: FirstName, * public readonly lastName: LastName, * public readonly age: Age, * ) { * super(); * } * } * * const person = new Person(new FirstName('John'), new LastName('Smith'), new Age(42)), * * person.toJSON() === { firstName: 'John', lastName: 'Smith', age: 42 } * * @returns {JSONValue} */ toJSON(): JSONValue | undefined { const fields = significantFieldsOf(this); if (fields.length === 1) { return toJSON(this[fields[0]]); } return fields.reduce((acc, field) => { acc[field] = toJSON(this[field]); return acc; }, {}) as JSONValue; } }