to-typed
Version:
Type-guards, casts and converts unknowns into typed values
256 lines • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cast = void 0;
const internal_js_1 = require("./internal.js");
class Cast {
constructor(_cast) {
this._cast = _cast;
}
static get build() {
return typeof __filename === 'undefined' ? 'esm' : 'cjs';
}
static unwrapArray(arr, s) {
switch (s.unwrapArray) {
case 'single':
return arr.length === 1 ? Cast.just(arr[0]) : Cast.nothing();
case 'first':
return arr.length > 0 ? Cast.just(arr[0]) : Cast.nothing();
case 'last':
return arr.length > 0 ? Cast.just(arr[arr.length - 1]) : Cast.nothing();
case 'never':
return Cast.nothing();
}
}
static wrapArray(val, s) {
switch (s.wrapArray) {
case 'single':
return Cast.just([val]);
case 'never':
return Cast.nothing();
}
}
static lazy(fun) {
return new Cast((val, s) => fun(s)._cast(val, s));
}
cast(value, settings) {
return this._cast(value, settings !== null && settings !== void 0 ? settings : Cast.defaults);
}
config(config) {
return new Cast((value, s) => this._cast(value, { ...s, ...config }));
}
static just(value) {
return new Cast(_ => internal_js_1.Maybe.just(value));
}
static nothing(error = Cast.castError) {
return new Cast(_ => internal_js_1.Maybe.nothing(error));
}
static try(get) {
try {
return Cast.just(get());
}
catch (e) {
return Cast.nothing(e instanceof Error ? e : typeof e === 'string' ? new Error(e) : Cast.castError);
}
}
parse(json) {
try {
return this.cast(JSON.parse(json));
}
catch (e) {
return internal_js_1.Maybe.nothing(e instanceof Error ? e : new Error('Unknown JSON.parse error'));
}
}
bind(next) {
return new Cast((value, s) => this._cast(value, s).bind(t => next(t, s)._cast(value, s)));
}
compose(next) {
return this.bind(value => new Cast((_, s) => next.cast(value, s)));
}
or(right) {
if (right instanceof internal_js_1.Convert)
return new internal_js_1.Convert((value, s) => this._cast(value, s).else(() => right.convert(value, s)));
else
return new Cast((value, s) => this.cast(value, s).or(right.cast(value, s)));
}
/**
* Unions a list of casts by combining them with the `or` operator.
* @param options An array of casts.
* @returns The union of the given casts.
*/
static some(...options) {
return options.reduce((acc, option) => acc.or(option), internal_js_1.Guard.isNever);
}
static all(casts) {
if (internal_js_1.Guard.isCollectionOf(internal_js_1.Guard.isInstanceOf(internal_js_1.Convert)).guard(casts)) {
const map = internal_js_1.Utils.mapLazy(casts);
return new internal_js_1.Convert((value, s) => map((conv) => conv.convert(value, s)));
}
else {
const map = internal_js_1.Utils.mapLazy(casts);
return new Cast((value, s) => internal_js_1.Maybe.all(map((cast) => cast.cast(value, s))));
}
}
/**
* Creates a convert that outputs an array containing the successful results of applying each cast in the given collection to the input value.
* @param casts An array of casts.
* @returns A convert that outputs an array of successfully results
*/
static any(casts) {
return new internal_js_1.Convert((value, s) => internal_js_1.Maybe.any(casts.map(cast => cast.cast(value, s))));
}
if(condition) {
return this.bind(value => condition(value) ? Cast.just(value) : Cast.nothing());
}
and(guard) {
return this.bind((value, s) => guard.guard(value, s) ? Cast.just(value) : Cast.nothing());
}
merge(cast) {
return this.bind(self => cast.bind(other => Cast.just({ ...self, ...other })));
}
map(next) {
return this.bind(value => Cast.just(next(value)));
}
else(other) {
return this.or(new internal_js_1.Convert(_ => other));
}
elseThrow(getError = e => e) {
return new internal_js_1.Convert((value, s) => this._cast(value, s).else(e => { throw getError(e); }));
}
get toMaybe() {
return new internal_js_1.Convert(value => this.cast(value));
}
static get asPrimitiveValue() {
return internal_js_1.Guard.isPrimitiveValue.or(internal_js_1.Guard.isArray
.bind(Cast.unwrapArray)
.compose(internal_js_1.Guard.isPrimitiveValue));
}
static get asString() {
return internal_js_1.Guard.isString.or(Cast.asPrimitiveValue.map(s => s.toString()));
}
static get asNumber() {
return Cast.asPrimitiveValue.compose(Cast.some(internal_js_1.Guard.isNumber, internal_js_1.Guard.isConst('NaN').map(() => NaN), internal_js_1.Guard.isString.map(parseFloat).if(n => !isNaN(n)), internal_js_1.Guard.isBigInt.if(n => Number.MIN_SAFE_INTEGER <= n && n <= Number.MAX_SAFE_INTEGER).map(n => Number(n)), internal_js_1.Guard.isBoolean.map(b => b ? 1 : 0)));
}
static get asFinite() {
return Cast.asNumber.and(internal_js_1.Guard.isFinite);
}
static get asInteger() {
return Cast.asFinite.map(Math.round);
}
static get asBigInt() {
return Cast.asPrimitiveValue.compose(Cast.some(internal_js_1.Guard.isBigInt, internal_js_1.Guard.isString.bind(s => Cast.try(() => BigInt(s))), internal_js_1.Guard.isSafeInteger.map(n => BigInt(n)), internal_js_1.Guard.isBoolean.map(b => BigInt(b ? 1 : 0))));
}
static get asBoolean() {
return internal_js_1.Guard.isBoolean.or(Cast.asString.bind((v, s) => {
if (s.booleanNames.true.includes(v))
return Cast.just(true);
else if (s.booleanNames.false.includes(v))
return Cast.just(false);
else
return Cast.nothing();
}));
}
static get asDate() {
return internal_js_1.Guard.some(internal_js_1.Guard.isInstanceOf(Date), internal_js_1.Guard.isString, internal_js_1.Guard.isSafeInteger)
.bind(s => Cast.try(() => new Date(s)))
.if(d => !isNaN(d.getTime()));
}
static get asArray() {
return internal_js_1.Guard.isArray.or(internal_js_1.Guard.isSomething.bind(Cast.wrapArray));
}
static asConst(value) {
return internal_js_1.Guard.isConst(value).or(Cast.just(value).asString.bind(str1 => Cast.asString.if(str2 => str1 === str2)).map(_ => value));
}
static asEnum(...options) {
return Cast.some(...options.map(Cast.asConst));
}
static makeCollectionOf(cast) {
return col => Cast.all(internal_js_1.Utils.mapEager(col, i => Cast.just(i).compose(cast)));
}
static asArrayOf(cast) {
return Cast.asArray.bind(Cast.makeCollectionOf(cast));
}
static asStructOf(cast) {
return internal_js_1.Guard.isStruct.bind(Cast.makeCollectionOf(cast));
}
static makeCollectionLike(casts) {
const map = internal_js_1.Utils.mapLazy(casts);
return col => Cast.all(map((cast, k) => Cast.just(col[k]).compose(cast)));
}
/**
* Given an object or tuple of casts, it produces a cast that outputs an object or tuple having the same shape as the given casts.
* @param casts an object or tuple of casts
* @returns a cast that produces an object or tuple matching the shape of the given casts
*/
static asCollectionLike(casts) {
return Array.isArray(casts) ?
Cast.asArray.bind(Cast.makeCollectionLike(casts)) :
internal_js_1.Guard.isStruct.bind(Cast.makeCollectionLike(casts));
}
/**
* Produces a cast that filters out values from the input that could not be casted by the given cast.
* @param cast the cast to use for filtering
* @returns a cast that filters out values that could not be casted by the given cast
*/
static asArrayWhere(cast) {
return Cast.asArray.bind(val => Cast.any(val.map(v => Cast.just(v).compose(cast))));
}
/**
* Creates a `Cast` based on a sample value.
* @param alt a sample value
* @returns a `Cast` based on the given sample value
*/
static as(alt) {
switch (typeof alt) {
case 'string':
return Cast.asString;
case 'number':
return Cast.asNumber;
case 'boolean':
return Cast.asBoolean;
case 'bigint':
return Cast.asBigInt;
case 'symbol':
return internal_js_1.Guard.isSymbol;
case 'undefined':
return internal_js_1.Guard.isConst(undefined);
case 'function':
return internal_js_1.Guard.isFunction;
case 'object':
if (alt instanceof Cast)
return alt;
else if (alt === null)
return internal_js_1.Guard.isConst(null);
}
return Cast.asCollectionLike(internal_js_1.Utils.mapEager(alt, Cast.as));
}
get asPrimitiveValue() { return this.compose(Cast.asPrimitiveValue); }
get asString() { return this.compose(Cast.asString); }
get asNumber() { return this.compose(Cast.asNumber); }
get asFinite() { return this.compose(Cast.asFinite); }
get asInteger() { return this.compose(Cast.asInteger); }
get asBigInt() { return this.compose(Cast.asBigInt); }
get asBoolean() { return this.compose(Cast.asBoolean); }
get asDate() { return this.compose(Cast.asDate); }
get asArray() { return this.compose(Cast.asArray); }
asConst(value) { return this.compose(Cast.asConst(value)); }
asEnum(...options) { return this.compose(Cast.asEnum(...options)); }
asArrayOf(cast) { return this.compose(Cast.asArrayOf(cast)); }
asStructOf(cast) { return this.compose(Cast.asStructOf(cast)); }
asCollectionLike(casts) { return this.compose(Cast.asCollectionLike(casts)); }
asArrayWhere(cast) { return this.compose(Cast.asArrayWhere(cast)); }
as(alt) { return this.compose(Cast.as(alt)); }
}
exports.Cast = Cast;
Cast.castError = new Error('Cast has no value');
Cast.defaults = {
keyGuarding: 'loose',
booleanNames: {
true: ['true', 'on', '1'],
false: ['false', 'off', '0']
},
unwrapArray: 'single',
wrapArray: 'single'
};
Cast.asUnknown = new Cast(value => internal_js_1.Maybe.just(value));
Cast.asNever = new Cast(_ => internal_js_1.Maybe.nothing(Cast.castError));
//# sourceMappingURL=cast.js.map