@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
227 lines • 8.38 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeBuilder = void 0;
const tslib_1 = require("tslib");
const schema = tslib_1.__importStar(require("../schema"));
const classes = tslib_1.__importStar(require("./classes"));
const { s } = schema;
class TypeBuilder {
constructor(system) {
this.system = system;
// --------------------------------------------------------------- shorthands
this.or = (...types) => this.Or(...types);
this.undefined = () => this.undef;
this.null = () => this.nil;
this.boolean = () => this.bool;
this.number = () => this.num;
this.string = () => this.str;
this.binary = () => this.bin;
this.con = (value, options) => this.Const(value, options);
this.literal = this.con;
this.array = (type, options) => this.Array((type ?? this.any), options);
this.tuple = (...types) => this.Tuple(types);
/**
* Creates an object type with the specified properties. This is a shorthand for
* `t.Object(t.prop(key, value), ...)`.
*
* Importantly, this method does not allow to specify object field order,
* so the order of properties in the resulting type is not guaranteed.
*
* Example:
*
* ```ts
* t.object({
* id: t.str,
* name: t.string(),
* age: t.num,
* verified: t.bool,
* });
* ```
*
* @param record A mapping of property names to types.
* @returns An object type.
*/
this.object = (record) => {
const keys = [];
for (const [key, value] of Object.entries(record))
keys.push(this.Key(key, value));
return new classes.ObjType(keys).sys(this.system);
};
/**
* Creates a type that represents a value that may be present or absent. The
* value is `undefined` if absent. This is a shorthand for `t.Or(type, t.undef)`.
*/
this.maybe = (type) => this.Or(type, this.undef);
/**
* Creates a union type from a list of values. This is a shorthand for
* `t.Or(t.Const(value1), t.Const(value2), ...)`. For example, the below
* are equivalent:
*
* ```ts
* t.enum('red', 'green', 'blue');
* t.Or(t.Const('red'), t.Const('green'), t.Const('blue'));
* ```
*
* @param values The values to include in the union.
* @returns A union type representing the values.
*/
this.enum = (...values) => this.Or(...values.map((type) => this.Const(type)));
}
// -------------------------------------------------------------- empty types
get any() {
return this.Any();
}
get undef() {
return this.Const(undefined);
}
get nil() {
return this.Const(null);
}
get bool() {
return this.Boolean();
}
get num() {
return this.Number();
}
get str() {
return this.String();
}
get bin() {
return this.Binary(this.any);
}
get arr() {
return this.Array(this.any);
}
get obj() {
return this.Object();
}
get map() {
return this.Map(this.any);
}
get fn() {
return this.Function(this.undef, this.undef);
}
get fn$() {
return this.Function$(this.undef, this.undef);
}
// --------------------------------------------------- base node constructors
Any(options) {
return new classes.AnyType(s.Any(options)).sys(this.system);
}
Const(value, options) {
return new classes.ConType(schema.s.Const(value, options)).sys(this.system);
}
Boolean(options) {
return new classes.BoolType(s.Boolean(options)).sys(this.system);
}
Number(options) {
return new classes.NumType(s.Number(options)).sys(this.system);
}
String(options) {
return new classes.StrType(s.String(options)).sys(this.system);
}
Binary(type, options = {}) {
return new classes.BinType(type, options).sys(this.system);
}
Array(type, options) {
return new classes.ArrType(type, void 0, void 0, options).sys(this.system);
}
Tuple(head, item, tail, options) {
return new classes.ArrType(item, head, tail, options).sys(this.system);
}
Object(...keys) {
return new classes.ObjType(keys).sys(this.system);
}
Key(key, value) {
return new classes.KeyType(key, value).sys(this.system);
}
KeyOpt(key, value) {
return new classes.KeyOptType(key, value).sys(this.system);
}
Map(val, key, options) {
return new classes.MapType(val, key, options).sys(this.system);
}
Or(...types) {
return new classes.OrType(types).sys(this.system);
}
Ref(ref) {
return new classes.RefType(ref).sys(this.system);
}
Function(req, res, options) {
return new classes.FnType(req, res, options).sys(this.system);
}
Function$(req, res, options) {
return new classes.FnRxType(req, res, options).sys(this.system);
}
import(node) {
switch (node.kind) {
case 'any':
return this.Any(node);
case 'bool':
return this.Boolean(node);
case 'num':
return this.Number(node);
case 'str':
return this.String(node);
case 'bin':
return this.Binary(this.import(node.type), node);
case 'arr': {
const { head, type, tail, ...rest } = node;
return this.Tuple(head ? head.map((h) => this.import(h)) : void 0, type ? this.import(type) : void 0, tail ? tail.map((t) => this.import(t)) : void 0, rest);
}
case 'obj': {
const fields = node.keys.map((f) => f.optional
? this.KeyOpt(f.key, this.import(f.value)).options(f)
: this.Key(f.key, this.import(f.value)).options(f));
return this.Object(...fields).options(node);
}
case 'key':
return node.optional
? this.KeyOpt(node.key, this.import(node.value)).options(node)
: this.Key(node.key, this.import(node.value)).options(node);
case 'map':
return this.Map(this.import(node.value), node.key ? this.import(node.key) : undefined, node);
case 'con':
return this.Const(node.value).options(node);
case 'or':
return this.Or(...node.types.map((t) => this.import(t))).options(node);
case 'ref':
return this.Ref(node.ref).options(node);
case 'fn':
return this.Function(this.import(node.req), this.import(node.res)).options(node);
case 'fn$':
return this.Function$(this.import(node.req), this.import(node.res)).options(node);
}
throw new Error(`UNKNOWN_NODE [${node.kind}]`);
}
from(value) {
switch (typeof value) {
case 'undefined':
return this.undef;
case 'boolean':
return this.bool;
case 'number':
return this.num;
case 'string':
return this.str;
case 'object':
if (value === null)
return this.nil;
if (Array.isArray(value)) {
if (value.length === 0)
return this.arr;
const getType = (v) => this.from(v) + '';
const allElementsOfTheSameType = value.every((v) => getType(v) === getType(value[0]));
this.Array(this.from(value[0]));
return allElementsOfTheSameType
? this.Array(this.from(value[0]))
: this.tuple(...value.map((v) => this.from(v)));
}
return this.Object(...Object.entries(value).map(([key, value]) => this.Key(key, this.from(value))));
default:
return this.any;
}
}
}
exports.TypeBuilder = TypeBuilder;
//# sourceMappingURL=TypeBuilder.js.map