@badrap/valita
Advanced tools
Comparing version 0.0.17 to 0.0.18
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.err = exports.ok = exports.undefined = exports.null = exports.union = exports.literal = exports.tuple = exports.array = exports.record = exports.object = exports.boolean = exports.string = exports.bigint = exports.number = exports.unknown = exports.never = exports.ValitaError = void 0; | ||
exports.err = exports.ok = exports.lazy = exports.undefined = exports.null = exports.union = exports.literal = exports.tuple = exports.array = exports.record = exports.object = exports.boolean = exports.string = exports.bigint = exports.number = exports.unknown = exports.never = exports.ValitaError = void 0; | ||
var tslib_1 = require("tslib"); | ||
@@ -136,2 +136,36 @@ function _collectIssues(tree, path, issues) { | ||
} | ||
var ValitaFailure = /** @class */ (function () { | ||
function ValitaFailure(issueTree) { | ||
this.issueTree = issueTree; | ||
this.ok = false; | ||
} | ||
Object.defineProperty(ValitaFailure.prototype, "issues", { | ||
get: function () { | ||
var issues = collectIssues(this.issueTree); | ||
Object.defineProperty(this, "issues", { | ||
value: issues, | ||
writable: false, | ||
}); | ||
return issues; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(ValitaFailure.prototype, "message", { | ||
get: function () { | ||
var message = formatIssueTree(this.issueTree); | ||
Object.defineProperty(this, "message", { | ||
value: message, | ||
writable: false, | ||
}); | ||
return message; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
ValitaFailure.prototype.throw = function () { | ||
throw new ValitaError(this.issueTree); | ||
}; | ||
return ValitaFailure; | ||
}()); | ||
var ValitaError = /** @class */ (function (_super) { | ||
@@ -163,3 +197,3 @@ tslib_1.__extends(ValitaError, _super); | ||
function joinIssues(left, right) { | ||
return right ? { code: "join", left: left, right: right } : left; | ||
return left ? { code: "join", left: left, right: right } : right; | ||
} | ||
@@ -195,6 +229,6 @@ function prependPath(key, tree) { | ||
exports.err = err; | ||
var Type = /** @class */ (function () { | ||
function Type() { | ||
var AbstractType = /** @class */ (function () { | ||
function AbstractType() { | ||
} | ||
Object.defineProperty(Type.prototype, "func", { | ||
Object.defineProperty(AbstractType.prototype, "func", { | ||
get: function () { | ||
@@ -211,3 +245,3 @@ var f = this.genFunc(); | ||
}); | ||
Type.prototype.parse = function (v, options) { | ||
AbstractType.prototype.parse = function (v, options) { | ||
var mode = FuncMode.PASS; | ||
@@ -231,15 +265,32 @@ if (options && options.mode === "strict") { | ||
}; | ||
Type.prototype.optional = function () { | ||
return new OptionalType(this); | ||
AbstractType.prototype.try = function (v, options) { | ||
var mode = FuncMode.PASS; | ||
if (options && options.mode === "strict") { | ||
mode = FuncMode.STRICT; | ||
} | ||
else if (options && options.mode === "strip") { | ||
mode = FuncMode.STRIP; | ||
} | ||
var r = this.func(v, mode); | ||
if (r === true) { | ||
return { ok: true, value: v }; | ||
} | ||
else if (r.code === "ok") { | ||
return { ok: true, value: r.value }; | ||
} | ||
else { | ||
return new ValitaFailure(r); | ||
} | ||
}; | ||
Type.prototype.default = function (defaultValue) { | ||
return this.optional().map(function (v) { | ||
return v === undefined ? defaultValue : v; | ||
}); | ||
AbstractType.prototype.optional = function () { | ||
return new Optional(this); | ||
}; | ||
Type.prototype.assert = function (func, error) { | ||
AbstractType.prototype.default = function (defaultValue) { | ||
return new Default(this, defaultValue); | ||
}; | ||
AbstractType.prototype.assert = function (func, error) { | ||
var err = { code: "custom_error", error: error }; | ||
return new TransformType(this, function (v) { return (func(v) ? true : err); }); | ||
}; | ||
Type.prototype.map = function (func) { | ||
AbstractType.prototype.map = function (func) { | ||
return new TransformType(this, function (v) { return ({ | ||
@@ -250,3 +301,3 @@ code: "ok", | ||
}; | ||
Type.prototype.chain = function (func) { | ||
AbstractType.prototype.chain = function (func) { | ||
return new TransformType(this, function (v) { | ||
@@ -262,4 +313,74 @@ var r = func(v); | ||
}; | ||
return AbstractType; | ||
}()); | ||
var Type = /** @class */ (function (_super) { | ||
tslib_1.__extends(Type, _super); | ||
function Type() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return Type; | ||
}()); | ||
}(AbstractType)); | ||
var Optional = /** @class */ (function (_super) { | ||
tslib_1.__extends(Optional, _super); | ||
function Optional(type) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.name = "optional"; | ||
return _this; | ||
} | ||
Optional.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
return function (v, mode) { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
}; | ||
Optional.prototype.toTerminals = function (into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
}; | ||
return Optional; | ||
}(AbstractType)); | ||
var Default = /** @class */ (function (_super) { | ||
tslib_1.__extends(Default, _super); | ||
function Default(type, defaultValue) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.defaultValue = defaultValue; | ||
_this.name = "default"; | ||
return _this; | ||
} | ||
Default.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
var undefinedOutput = this.defaultValue === undefined | ||
? true | ||
: { code: "ok", value: this.defaultValue }; | ||
var nothingOutput = { | ||
code: "ok", | ||
value: this.defaultValue, | ||
}; | ||
return function (v, mode) { | ||
if (v === undefined) { | ||
return undefinedOutput; | ||
} | ||
else if (v === Nothing) { | ||
return nothingOutput; | ||
} | ||
else { | ||
var result = func(v, mode); | ||
if (result !== true && | ||
result.code === "ok" && | ||
result.value === undefined) { | ||
return nothingOutput; | ||
} | ||
return result; | ||
} | ||
}; | ||
}; | ||
Default.prototype.toTerminals = function (into) { | ||
into.push(this.type.optional()); | ||
this.type.toTerminals(into); | ||
}; | ||
return Default; | ||
}(Type)); | ||
var ObjectType = /** @class */ (function (_super) { | ||
@@ -325,3 +446,3 @@ tslib_1.__extends(ObjectType, _super); | ||
if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -367,3 +488,3 @@ else if (!issueTree) { | ||
else if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -422,7 +543,8 @@ else if (!issueTree) { | ||
var _this = this; | ||
var _a; | ||
var shape = {}; | ||
var rest = this.restType && this.restType.optional(); | ||
Object.keys(this.shape).forEach(function (key) { | ||
shape[key] = _this.shape[key].optional(); | ||
}); | ||
var rest = (_a = this.restType) === null || _a === void 0 ? void 0 : _a.optional(); | ||
return new ObjectType(shape, rest); | ||
@@ -477,3 +599,3 @@ }; | ||
else { | ||
issueTree = joinIssues(prependPath(i, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(i, r)); | ||
} | ||
@@ -710,3 +832,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -731,3 +853,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -787,2 +909,5 @@ } | ||
}; | ||
UnionType.prototype.optional = function () { | ||
return new Optional(this); | ||
}; | ||
return UnionType; | ||
@@ -935,23 +1060,2 @@ }(Type)); | ||
}(Type)); | ||
var OptionalType = /** @class */ (function (_super) { | ||
tslib_1.__extends(OptionalType, _super); | ||
function OptionalType(type) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.name = "optional"; | ||
return _this; | ||
} | ||
OptionalType.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
return function (v, mode) { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
}; | ||
OptionalType.prototype.toTerminals = function (into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
}; | ||
return OptionalType; | ||
}(Type)); | ||
var TransformType = /** @class */ (function (_super) { | ||
@@ -1011,2 +1115,30 @@ tslib_1.__extends(TransformType, _super); | ||
}(Type)); | ||
var LazyType = /** @class */ (function (_super) { | ||
tslib_1.__extends(LazyType, _super); | ||
function LazyType(definer) { | ||
var _this = _super.call(this) || this; | ||
_this.definer = definer; | ||
_this.name = "lazy"; | ||
return _this; | ||
} | ||
Object.defineProperty(LazyType.prototype, "type", { | ||
get: function () { | ||
var type = this.definer(); | ||
Object.defineProperty(this, "type", { | ||
value: type, | ||
writable: false, | ||
}); | ||
return type; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
LazyType.prototype.genFunc = function () { | ||
return this.type.genFunc(); | ||
}; | ||
LazyType.prototype.toTerminals = function (into) { | ||
this.type.toTerminals(into); | ||
}; | ||
return LazyType; | ||
}(Type)); | ||
function never() { | ||
@@ -1072,2 +1204,6 @@ return new NeverType(); | ||
exports.union = union; | ||
function lazy(definer) { | ||
return new LazyType(definer); | ||
} | ||
exports.lazy = lazy; | ||
//# sourceMappingURL=index.js.map |
@@ -133,2 +133,36 @@ import { __assign, __extends, __spreadArray } from "tslib"; | ||
} | ||
var ValitaFailure = /** @class */ (function () { | ||
function ValitaFailure(issueTree) { | ||
this.issueTree = issueTree; | ||
this.ok = false; | ||
} | ||
Object.defineProperty(ValitaFailure.prototype, "issues", { | ||
get: function () { | ||
var issues = collectIssues(this.issueTree); | ||
Object.defineProperty(this, "issues", { | ||
value: issues, | ||
writable: false, | ||
}); | ||
return issues; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(ValitaFailure.prototype, "message", { | ||
get: function () { | ||
var message = formatIssueTree(this.issueTree); | ||
Object.defineProperty(this, "message", { | ||
value: message, | ||
writable: false, | ||
}); | ||
return message; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
ValitaFailure.prototype.throw = function () { | ||
throw new ValitaError(this.issueTree); | ||
}; | ||
return ValitaFailure; | ||
}()); | ||
var ValitaError = /** @class */ (function (_super) { | ||
@@ -160,3 +194,3 @@ __extends(ValitaError, _super); | ||
function joinIssues(left, right) { | ||
return right ? { code: "join", left: left, right: right } : left; | ||
return left ? { code: "join", left: left, right: right } : right; | ||
} | ||
@@ -190,6 +224,6 @@ function prependPath(key, tree) { | ||
} | ||
var Type = /** @class */ (function () { | ||
function Type() { | ||
var AbstractType = /** @class */ (function () { | ||
function AbstractType() { | ||
} | ||
Object.defineProperty(Type.prototype, "func", { | ||
Object.defineProperty(AbstractType.prototype, "func", { | ||
get: function () { | ||
@@ -206,3 +240,3 @@ var f = this.genFunc(); | ||
}); | ||
Type.prototype.parse = function (v, options) { | ||
AbstractType.prototype.parse = function (v, options) { | ||
var mode = FuncMode.PASS; | ||
@@ -226,15 +260,32 @@ if (options && options.mode === "strict") { | ||
}; | ||
Type.prototype.optional = function () { | ||
return new OptionalType(this); | ||
AbstractType.prototype.try = function (v, options) { | ||
var mode = FuncMode.PASS; | ||
if (options && options.mode === "strict") { | ||
mode = FuncMode.STRICT; | ||
} | ||
else if (options && options.mode === "strip") { | ||
mode = FuncMode.STRIP; | ||
} | ||
var r = this.func(v, mode); | ||
if (r === true) { | ||
return { ok: true, value: v }; | ||
} | ||
else if (r.code === "ok") { | ||
return { ok: true, value: r.value }; | ||
} | ||
else { | ||
return new ValitaFailure(r); | ||
} | ||
}; | ||
Type.prototype.default = function (defaultValue) { | ||
return this.optional().map(function (v) { | ||
return v === undefined ? defaultValue : v; | ||
}); | ||
AbstractType.prototype.optional = function () { | ||
return new Optional(this); | ||
}; | ||
Type.prototype.assert = function (func, error) { | ||
AbstractType.prototype.default = function (defaultValue) { | ||
return new Default(this, defaultValue); | ||
}; | ||
AbstractType.prototype.assert = function (func, error) { | ||
var err = { code: "custom_error", error: error }; | ||
return new TransformType(this, function (v) { return (func(v) ? true : err); }); | ||
}; | ||
Type.prototype.map = function (func) { | ||
AbstractType.prototype.map = function (func) { | ||
return new TransformType(this, function (v) { return ({ | ||
@@ -245,3 +296,3 @@ code: "ok", | ||
}; | ||
Type.prototype.chain = function (func) { | ||
AbstractType.prototype.chain = function (func) { | ||
return new TransformType(this, function (v) { | ||
@@ -257,4 +308,74 @@ var r = func(v); | ||
}; | ||
return AbstractType; | ||
}()); | ||
var Type = /** @class */ (function (_super) { | ||
__extends(Type, _super); | ||
function Type() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return Type; | ||
}()); | ||
}(AbstractType)); | ||
var Optional = /** @class */ (function (_super) { | ||
__extends(Optional, _super); | ||
function Optional(type) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.name = "optional"; | ||
return _this; | ||
} | ||
Optional.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
return function (v, mode) { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
}; | ||
Optional.prototype.toTerminals = function (into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
}; | ||
return Optional; | ||
}(AbstractType)); | ||
var Default = /** @class */ (function (_super) { | ||
__extends(Default, _super); | ||
function Default(type, defaultValue) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.defaultValue = defaultValue; | ||
_this.name = "default"; | ||
return _this; | ||
} | ||
Default.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
var undefinedOutput = this.defaultValue === undefined | ||
? true | ||
: { code: "ok", value: this.defaultValue }; | ||
var nothingOutput = { | ||
code: "ok", | ||
value: this.defaultValue, | ||
}; | ||
return function (v, mode) { | ||
if (v === undefined) { | ||
return undefinedOutput; | ||
} | ||
else if (v === Nothing) { | ||
return nothingOutput; | ||
} | ||
else { | ||
var result = func(v, mode); | ||
if (result !== true && | ||
result.code === "ok" && | ||
result.value === undefined) { | ||
return nothingOutput; | ||
} | ||
return result; | ||
} | ||
}; | ||
}; | ||
Default.prototype.toTerminals = function (into) { | ||
into.push(this.type.optional()); | ||
this.type.toTerminals(into); | ||
}; | ||
return Default; | ||
}(Type)); | ||
var ObjectType = /** @class */ (function (_super) { | ||
@@ -320,3 +441,3 @@ __extends(ObjectType, _super); | ||
if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -362,3 +483,3 @@ else if (!issueTree) { | ||
else if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -417,7 +538,8 @@ else if (!issueTree) { | ||
var _this = this; | ||
var _a; | ||
var shape = {}; | ||
var rest = this.restType && this.restType.optional(); | ||
Object.keys(this.shape).forEach(function (key) { | ||
shape[key] = _this.shape[key].optional(); | ||
}); | ||
var rest = (_a = this.restType) === null || _a === void 0 ? void 0 : _a.optional(); | ||
return new ObjectType(shape, rest); | ||
@@ -472,3 +594,3 @@ }; | ||
else { | ||
issueTree = joinIssues(prependPath(i, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(i, r)); | ||
} | ||
@@ -705,3 +827,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -726,3 +848,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -782,2 +904,5 @@ } | ||
}; | ||
UnionType.prototype.optional = function () { | ||
return new Optional(this); | ||
}; | ||
return UnionType; | ||
@@ -930,23 +1055,2 @@ }(Type)); | ||
}(Type)); | ||
var OptionalType = /** @class */ (function (_super) { | ||
__extends(OptionalType, _super); | ||
function OptionalType(type) { | ||
var _this = _super.call(this) || this; | ||
_this.type = type; | ||
_this.name = "optional"; | ||
return _this; | ||
} | ||
OptionalType.prototype.genFunc = function () { | ||
var func = this.type.func; | ||
return function (v, mode) { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
}; | ||
OptionalType.prototype.toTerminals = function (into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
}; | ||
return OptionalType; | ||
}(Type)); | ||
var TransformType = /** @class */ (function (_super) { | ||
@@ -1006,2 +1110,30 @@ __extends(TransformType, _super); | ||
}(Type)); | ||
var LazyType = /** @class */ (function (_super) { | ||
__extends(LazyType, _super); | ||
function LazyType(definer) { | ||
var _this = _super.call(this) || this; | ||
_this.definer = definer; | ||
_this.name = "lazy"; | ||
return _this; | ||
} | ||
Object.defineProperty(LazyType.prototype, "type", { | ||
get: function () { | ||
var type = this.definer(); | ||
Object.defineProperty(this, "type", { | ||
value: type, | ||
writable: false, | ||
}); | ||
return type; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
LazyType.prototype.genFunc = function () { | ||
return this.type.genFunc(); | ||
}; | ||
LazyType.prototype.toTerminals = function (into) { | ||
this.type.toTerminals(into); | ||
}; | ||
return LazyType; | ||
}(Type)); | ||
function never() { | ||
@@ -1053,3 +1185,6 @@ return new NeverType(); | ||
} | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, ok, err, }; | ||
function lazy(definer) { | ||
return new LazyType(definer); | ||
} | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, lazy, ok, err, }; | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.err = exports.ok = exports.undefined = exports.null = exports.union = exports.literal = exports.tuple = exports.array = exports.record = exports.object = exports.boolean = exports.string = exports.bigint = exports.number = exports.unknown = exports.never = exports.ValitaError = void 0; | ||
exports.err = exports.ok = exports.lazy = exports.undefined = exports.null = exports.union = exports.literal = exports.tuple = exports.array = exports.record = exports.object = exports.boolean = exports.string = exports.bigint = exports.number = exports.unknown = exports.never = exports.ValitaError = void 0; | ||
function _collectIssues(tree, path, issues) { | ||
@@ -134,2 +134,27 @@ var _a; | ||
} | ||
class ValitaFailure { | ||
constructor(issueTree) { | ||
this.issueTree = issueTree; | ||
this.ok = false; | ||
} | ||
get issues() { | ||
const issues = collectIssues(this.issueTree); | ||
Object.defineProperty(this, "issues", { | ||
value: issues, | ||
writable: false, | ||
}); | ||
return issues; | ||
} | ||
get message() { | ||
const message = formatIssueTree(this.issueTree); | ||
Object.defineProperty(this, "message", { | ||
value: message, | ||
writable: false, | ||
}); | ||
return message; | ||
} | ||
throw() { | ||
throw new ValitaError(this.issueTree); | ||
} | ||
} | ||
class ValitaError extends Error { | ||
@@ -153,3 +178,3 @@ constructor(issueTree) { | ||
function joinIssues(left, right) { | ||
return right ? { code: "join", left, right } : left; | ||
return left ? { code: "join", left, right } : right; | ||
} | ||
@@ -185,3 +210,3 @@ function prependPath(key, tree) { | ||
exports.err = err; | ||
class Type { | ||
class AbstractType { | ||
get func() { | ||
@@ -214,7 +239,26 @@ const f = this.genFunc(); | ||
} | ||
try(v, options) { | ||
let mode = FuncMode.PASS; | ||
if (options && options.mode === "strict") { | ||
mode = FuncMode.STRICT; | ||
} | ||
else if (options && options.mode === "strip") { | ||
mode = FuncMode.STRIP; | ||
} | ||
const r = this.func(v, mode); | ||
if (r === true) { | ||
return { ok: true, value: v }; | ||
} | ||
else if (r.code === "ok") { | ||
return { ok: true, value: r.value }; | ||
} | ||
else { | ||
return new ValitaFailure(r); | ||
} | ||
} | ||
optional() { | ||
return new OptionalType(this); | ||
return new Optional(this); | ||
} | ||
default(defaultValue) { | ||
return this.optional().map((v) => v === undefined ? defaultValue : v); | ||
return new Default(this, defaultValue); | ||
} | ||
@@ -243,2 +287,61 @@ assert(func, error) { | ||
} | ||
class Type extends AbstractType { | ||
} | ||
class Optional extends AbstractType { | ||
constructor(type) { | ||
super(); | ||
this.type = type; | ||
this.name = "optional"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class Default extends Type { | ||
constructor(type, defaultValue) { | ||
super(); | ||
this.type = type; | ||
this.defaultValue = defaultValue; | ||
this.name = "default"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
const undefinedOutput = this.defaultValue === undefined | ||
? true | ||
: { code: "ok", value: this.defaultValue }; | ||
const nothingOutput = { | ||
code: "ok", | ||
value: this.defaultValue, | ||
}; | ||
return (v, mode) => { | ||
if (v === undefined) { | ||
return undefinedOutput; | ||
} | ||
else if (v === Nothing) { | ||
return nothingOutput; | ||
} | ||
else { | ||
const result = func(v, mode); | ||
if (result !== true && | ||
result.code === "ok" && | ||
result.value === undefined) { | ||
return nothingOutput; | ||
} | ||
return result; | ||
} | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this.type.optional()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class ObjectType extends Type { | ||
@@ -303,3 +406,3 @@ constructor(shape, restType, checks) { | ||
if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -345,3 +448,3 @@ else if (!issueTree) { | ||
else if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -390,7 +493,8 @@ else if (!issueTree) { | ||
partial() { | ||
var _a; | ||
const shape = {}; | ||
const rest = this.restType && this.restType.optional(); | ||
Object.keys(this.shape).forEach((key) => { | ||
shape[key] = this.shape[key].optional(); | ||
}); | ||
const rest = (_a = this.restType) === null || _a === void 0 ? void 0 : _a.optional(); | ||
return new ObjectType(shape, rest); | ||
@@ -442,3 +546,3 @@ } | ||
else { | ||
issueTree = joinIssues(prependPath(i, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(i, r)); | ||
} | ||
@@ -661,3 +765,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -682,3 +786,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -733,2 +837,5 @@ } | ||
} | ||
optional() { | ||
return new Optional(this); | ||
} | ||
} | ||
@@ -853,20 +960,2 @@ class NeverType extends Type { | ||
} | ||
class OptionalType extends Type { | ||
constructor(type) { | ||
super(); | ||
this.type = type; | ||
this.name = "optional"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class TransformType extends Type { | ||
@@ -923,2 +1012,23 @@ constructor(transformed, transform) { | ||
} | ||
class LazyType extends Type { | ||
constructor(definer) { | ||
super(); | ||
this.definer = definer; | ||
this.name = "lazy"; | ||
} | ||
get type() { | ||
const type = this.definer(); | ||
Object.defineProperty(this, "type", { | ||
value: type, | ||
writable: false, | ||
}); | ||
return type; | ||
} | ||
genFunc() { | ||
return this.type.genFunc(); | ||
} | ||
toTerminals(into) { | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
function never() { | ||
@@ -980,2 +1090,6 @@ return new NeverType(); | ||
exports.union = union; | ||
function lazy(definer) { | ||
return new LazyType(definer); | ||
} | ||
exports.lazy = lazy; | ||
//# sourceMappingURL=index.js.map |
@@ -131,2 +131,27 @@ function _collectIssues(tree, path, issues) { | ||
} | ||
class ValitaFailure { | ||
constructor(issueTree) { | ||
this.issueTree = issueTree; | ||
this.ok = false; | ||
} | ||
get issues() { | ||
const issues = collectIssues(this.issueTree); | ||
Object.defineProperty(this, "issues", { | ||
value: issues, | ||
writable: false, | ||
}); | ||
return issues; | ||
} | ||
get message() { | ||
const message = formatIssueTree(this.issueTree); | ||
Object.defineProperty(this, "message", { | ||
value: message, | ||
writable: false, | ||
}); | ||
return message; | ||
} | ||
throw() { | ||
throw new ValitaError(this.issueTree); | ||
} | ||
} | ||
export class ValitaError extends Error { | ||
@@ -149,3 +174,3 @@ constructor(issueTree) { | ||
function joinIssues(left, right) { | ||
return right ? { code: "join", left, right } : left; | ||
return left ? { code: "join", left, right } : right; | ||
} | ||
@@ -179,3 +204,3 @@ function prependPath(key, tree) { | ||
} | ||
class Type { | ||
class AbstractType { | ||
get func() { | ||
@@ -208,7 +233,26 @@ const f = this.genFunc(); | ||
} | ||
try(v, options) { | ||
let mode = FuncMode.PASS; | ||
if (options && options.mode === "strict") { | ||
mode = FuncMode.STRICT; | ||
} | ||
else if (options && options.mode === "strip") { | ||
mode = FuncMode.STRIP; | ||
} | ||
const r = this.func(v, mode); | ||
if (r === true) { | ||
return { ok: true, value: v }; | ||
} | ||
else if (r.code === "ok") { | ||
return { ok: true, value: r.value }; | ||
} | ||
else { | ||
return new ValitaFailure(r); | ||
} | ||
} | ||
optional() { | ||
return new OptionalType(this); | ||
return new Optional(this); | ||
} | ||
default(defaultValue) { | ||
return this.optional().map((v) => v === undefined ? defaultValue : v); | ||
return new Default(this, defaultValue); | ||
} | ||
@@ -237,2 +281,61 @@ assert(func, error) { | ||
} | ||
class Type extends AbstractType { | ||
} | ||
class Optional extends AbstractType { | ||
constructor(type) { | ||
super(); | ||
this.type = type; | ||
this.name = "optional"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class Default extends Type { | ||
constructor(type, defaultValue) { | ||
super(); | ||
this.type = type; | ||
this.defaultValue = defaultValue; | ||
this.name = "default"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
const undefinedOutput = this.defaultValue === undefined | ||
? true | ||
: { code: "ok", value: this.defaultValue }; | ||
const nothingOutput = { | ||
code: "ok", | ||
value: this.defaultValue, | ||
}; | ||
return (v, mode) => { | ||
if (v === undefined) { | ||
return undefinedOutput; | ||
} | ||
else if (v === Nothing) { | ||
return nothingOutput; | ||
} | ||
else { | ||
const result = func(v, mode); | ||
if (result !== true && | ||
result.code === "ok" && | ||
result.value === undefined) { | ||
return nothingOutput; | ||
} | ||
return result; | ||
} | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this.type.optional()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class ObjectType extends Type { | ||
@@ -297,3 +400,3 @@ constructor(shape, restType, checks) { | ||
if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -339,3 +442,3 @@ else if (!issueTree) { | ||
else if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} | ||
@@ -384,7 +487,8 @@ else if (!issueTree) { | ||
partial() { | ||
var _a; | ||
const shape = {}; | ||
const rest = this.restType && this.restType.optional(); | ||
Object.keys(this.shape).forEach((key) => { | ||
shape[key] = this.shape[key].optional(); | ||
}); | ||
const rest = (_a = this.restType) === null || _a === void 0 ? void 0 : _a.optional(); | ||
return new ObjectType(shape, rest); | ||
@@ -436,3 +540,3 @@ } | ||
else { | ||
issueTree = joinIssues(prependPath(i, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(i, r)); | ||
} | ||
@@ -655,3 +759,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -676,3 +780,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -727,2 +831,5 @@ } | ||
} | ||
optional() { | ||
return new Optional(this); | ||
} | ||
} | ||
@@ -847,20 +954,2 @@ class NeverType extends Type { | ||
} | ||
class OptionalType extends Type { | ||
constructor(type) { | ||
super(); | ||
this.type = type; | ||
this.name = "optional"; | ||
} | ||
genFunc() { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into) { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class TransformType extends Type { | ||
@@ -917,2 +1006,23 @@ constructor(transformed, transform) { | ||
} | ||
class LazyType extends Type { | ||
constructor(definer) { | ||
super(); | ||
this.definer = definer; | ||
this.name = "lazy"; | ||
} | ||
get type() { | ||
const type = this.definer(); | ||
Object.defineProperty(this, "type", { | ||
value: type, | ||
writable: false, | ||
}); | ||
return type; | ||
} | ||
genFunc() { | ||
return this.type.genFunc(); | ||
} | ||
toTerminals(into) { | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
function never() { | ||
@@ -960,3 +1070,6 @@ return new NeverType(); | ||
} | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, ok, err, }; | ||
function lazy(definer) { | ||
return new LazyType(definer); | ||
} | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, lazy, ok, err, }; | ||
//# sourceMappingURL=index.js.map |
@@ -40,2 +40,11 @@ declare type PrettyIntersection<V> = Extract<{ | ||
}> | Issue; | ||
export declare type ValitaResult<V> = Readonly<{ | ||
ok: true; | ||
value: V; | ||
}> | Readonly<{ | ||
ok: false; | ||
message: string; | ||
issues: readonly Issue[]; | ||
throw(): never; | ||
}>; | ||
export declare class ValitaError extends Error { | ||
@@ -46,7 +55,6 @@ private readonly issueTree; | ||
} | ||
declare type Ok<T> = true | Readonly<{ | ||
declare type RawResult<T> = true | Readonly<{ | ||
code: "ok"; | ||
value: T; | ||
}>; | ||
declare type Result<T> = Ok<T> | IssueTree; | ||
}> | IssueTree; | ||
declare const enum FuncMode { | ||
@@ -57,3 +65,3 @@ PASS = 0, | ||
} | ||
declare type Func<T> = (v: unknown, mode: FuncMode) => Result<T>; | ||
declare type Func<T> = (v: unknown, mode: FuncMode) => RawResult<T>; | ||
declare type ParseOptions = { | ||
@@ -81,9 +89,4 @@ mode: "passthrough" | "strict" | "strip"; | ||
}; | ||
declare type DefaultOutput<Output, DefaultValue> = Type<Exclude<Output, undefined> | DefaultValue>; | ||
declare const isOptional: unique symbol; | ||
declare class Optional { | ||
protected readonly [isOptional]: true; | ||
} | ||
declare type IfOptional<T extends Type, Then, Else> = T extends Optional ? Then : Else; | ||
declare abstract class Type<Output = unknown> { | ||
export declare type Infer<T extends AbstractType> = T extends AbstractType<infer I> ? I : never; | ||
declare abstract class AbstractType<Output = unknown> { | ||
abstract readonly name: string; | ||
@@ -93,6 +96,7 @@ abstract genFunc(): Func<Output>; | ||
get func(): Func<Output>; | ||
parse<T extends Type>(this: T, v: unknown, options?: Partial<ParseOptions>): Infer<T>; | ||
optional(): Type<Output | undefined> & Optional; | ||
default<T extends Literal>(defaultValue: T): DefaultOutput<Output, T>; | ||
default<T>(defaultValue: T): DefaultOutput<Output, T>; | ||
parse<T extends AbstractType>(this: T, v: unknown, options?: Partial<ParseOptions>): Infer<T>; | ||
try<T extends AbstractType>(this: T, v: unknown, options?: Partial<ParseOptions>): ValitaResult<Infer<T>>; | ||
optional(): Optional<Output>; | ||
default<T extends Literal>(defaultValue: T): Default<Output, T>; | ||
default<T>(defaultValue: T): Default<Output, T>; | ||
assert<T extends Output>(func: ((v: Output) => v is T) | ((v: Output) => boolean), error?: CustomError): Type<T>; | ||
@@ -104,8 +108,28 @@ map<T extends Literal>(func: (v: Output) => T): Type<T>; | ||
} | ||
export declare type Infer<T extends Type> = T extends Type<infer I> ? I : never; | ||
declare type ObjectShape = Record<string, Type>; | ||
declare const isOptional: unique symbol; | ||
declare type IfOptional<T extends AbstractType, Then, Else> = T extends Optional ? Then : Else; | ||
declare abstract class Type<Output = unknown> extends AbstractType<Output> { | ||
protected readonly [isOptional] = false; | ||
} | ||
declare class Optional<Output = unknown> extends AbstractType<Output | undefined> { | ||
private readonly type; | ||
protected readonly [isOptional] = true; | ||
readonly name = "optional"; | ||
constructor(type: AbstractType<Output>); | ||
genFunc(): Func<Output | undefined>; | ||
toTerminals(into: TerminalType[]): void; | ||
} | ||
declare class Default<Output, DefaultValue> extends Type<Exclude<Output, undefined> | DefaultValue> { | ||
private readonly type; | ||
private readonly defaultValue; | ||
readonly name = "default"; | ||
constructor(type: AbstractType<Output>, defaultValue: DefaultValue); | ||
genFunc(): Func<Exclude<Output, undefined> | DefaultValue>; | ||
toTerminals(into: TerminalType[]): void; | ||
} | ||
declare type ObjectShape = Record<string, AbstractType>; | ||
declare type Optionals<T extends ObjectShape> = { | ||
[K in keyof T]: IfOptional<T[K], K, never>; | ||
}[keyof T]; | ||
declare type ObjectOutput<T extends ObjectShape, R extends Type | undefined> = PrettyIntersection<{ | ||
declare type ObjectOutput<T extends ObjectShape, R extends AbstractType | undefined> = PrettyIntersection<{ | ||
[K in Optionals<T>]?: Infer<T[K]>; | ||
@@ -116,4 +140,6 @@ } & { | ||
[K: string]: I; | ||
} : unknown)>; | ||
declare class ObjectType<Shape extends ObjectShape = ObjectShape, Rest extends Type | undefined = Type | undefined> extends Type<ObjectOutput<Shape, Rest>> { | ||
} : R extends Optional<infer J> ? Partial<{ | ||
[K: string]: J; | ||
}> : unknown)>; | ||
declare class ObjectType<Shape extends ObjectShape = ObjectShape, Rest extends AbstractType | undefined = AbstractType | undefined> extends Type<ObjectOutput<Shape, Rest>> { | ||
readonly shape: Shape; | ||
@@ -135,4 +161,4 @@ private readonly restType; | ||
partial(): ObjectType<{ | ||
[K in keyof Shape]: OptionalType<Infer<Shape[K]>> & Optional; | ||
}, Rest extends Type<infer I> ? OptionalType<I> & Optional : undefined>; | ||
[K in keyof Shape]: Optional<Infer<Shape[K]>>; | ||
}, Rest extends AbstractType<infer I> ? Optional<I> : undefined>; | ||
} | ||
@@ -154,9 +180,2 @@ declare type TupleOutput<T extends Type[]> = { | ||
} | ||
declare class UnionType<T extends Type[] = Type[]> extends Type<Infer<T[number]>> { | ||
readonly options: T; | ||
readonly name = "union"; | ||
constructor(options: T); | ||
toTerminals(into: TerminalType[]): void; | ||
genFunc(): Func<Infer<T[number]>>; | ||
} | ||
declare class NeverType extends Type<never> { | ||
@@ -209,9 +228,2 @@ readonly name = "never"; | ||
} | ||
declare class OptionalType<Output = unknown> extends Type<Output | undefined> { | ||
private readonly type; | ||
readonly name = "optional"; | ||
constructor(type: Type<Output>); | ||
genFunc(): Func<Output>; | ||
toTerminals(into: TerminalType[]): void; | ||
} | ||
declare function never(): NeverType; | ||
@@ -225,3 +237,3 @@ declare function unknown(): UnknownType; | ||
declare function null_(): NullType; | ||
declare function object<T extends Record<string, Type>>(obj: T): ObjectType<T, undefined>; | ||
declare function object<T extends Record<string, Type | Optional>>(obj: T): ObjectType<T, undefined>; | ||
declare function record<T extends Type>(valueType: T): Type<Record<string, Infer<T>>>; | ||
@@ -231,6 +243,7 @@ declare function array<T extends Type>(item: T): ArrayType<[], T>; | ||
declare function literal<T extends Literal>(value: T): LiteralType<T>; | ||
declare function union<T extends Type[]>(...options: T): UnionType<T> & (true extends IfOptional<T[number], true, false> ? Optional : unknown); | ||
declare type TerminalType = NeverType | UnknownType | StringType | NumberType | BigIntType | BooleanType | UndefinedType | NullType | ObjectType | ArrayType | LiteralType | OptionalType; | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, ok, err, }; | ||
export type { Type }; | ||
declare function union<T extends Type[]>(...options: T): Type<Infer<T[number]>>; | ||
declare function lazy<T>(definer: () => Type<T>): Type<T>; | ||
declare type TerminalType = NeverType | UnknownType | StringType | NumberType | BigIntType | BooleanType | UndefinedType | NullType | ObjectType | ArrayType | LiteralType | Optional; | ||
export { never, unknown, number, bigint, string, boolean, object, record, array, tuple, literal, union, null_ as null, undefined_ as undefined, lazy, ok, err, }; | ||
export type { Type, Optional }; | ||
//# sourceMappingURL=index.d.ts.map |
{ | ||
"name": "@badrap/valita", | ||
"version": "0.0.17", | ||
"version": "0.0.18", | ||
"description": "A validation & parsing library for TypeScript", | ||
@@ -5,0 +5,0 @@ "main": "./dist/cjs/index.js", |
326
src/index.ts
@@ -173,2 +173,42 @@ // This is magic that turns object intersections to nicer-looking types. | ||
export type ValitaResult<V> = | ||
| Readonly<{ | ||
ok: true; | ||
value: V; | ||
}> | ||
| Readonly<{ | ||
ok: false; | ||
message: string; | ||
issues: readonly Issue[]; | ||
throw(): never; | ||
}>; | ||
class ValitaFailure { | ||
readonly ok = false; | ||
constructor(private readonly issueTree: IssueTree) {} | ||
get issues(): readonly Issue[] { | ||
const issues = collectIssues(this.issueTree); | ||
Object.defineProperty(this, "issues", { | ||
value: issues, | ||
writable: false, | ||
}); | ||
return issues; | ||
} | ||
get message(): string { | ||
const message = formatIssueTree(this.issueTree); | ||
Object.defineProperty(this, "message", { | ||
value: message, | ||
writable: false, | ||
}); | ||
return message; | ||
} | ||
throw(): never { | ||
throw new ValitaError(this.issueTree); | ||
} | ||
} | ||
export class ValitaError extends Error { | ||
@@ -191,4 +231,4 @@ constructor(private readonly issueTree: IssueTree) { | ||
function joinIssues(left: IssueTree, right: IssueTree | undefined): IssueTree { | ||
return right ? { code: "join", left, right } : left; | ||
function joinIssues(left: IssueTree | undefined, right: IssueTree): IssueTree { | ||
return left ? { code: "join", left, right } : right; | ||
} | ||
@@ -200,9 +240,3 @@ | ||
type Ok<T> = | ||
| true | ||
| Readonly<{ | ||
code: "ok"; | ||
value: T; | ||
}>; | ||
type Result<T> = Ok<T> | IssueTree; | ||
type RawResult<T> = true | Readonly<{ code: "ok"; value: T }> | IssueTree; | ||
@@ -213,3 +247,3 @@ function isObject(v: unknown): v is Record<string, unknown> { | ||
function toTerminals(type: Type): TerminalType[] { | ||
function toTerminals(type: AbstractType): TerminalType[] { | ||
const result: TerminalType[] = []; | ||
@@ -220,3 +254,3 @@ type.toTerminals(result); | ||
function hasTerminal(type: Type, name: TerminalType["name"]): boolean { | ||
function hasTerminal(type: AbstractType, name: TerminalType["name"]): boolean { | ||
return toTerminals(type).some((t) => t.name === name); | ||
@@ -232,3 +266,3 @@ } | ||
} | ||
type Func<T> = (v: unknown, mode: FuncMode) => Result<T>; | ||
type Func<T> = (v: unknown, mode: FuncMode) => RawResult<T>; | ||
@@ -261,13 +295,7 @@ type ParseOptions = { | ||
type DefaultOutput<Output, DefaultValue> = Type< | ||
Exclude<Output, undefined> | DefaultValue | ||
>; | ||
export type Infer<T extends AbstractType> = T extends AbstractType<infer I> | ||
? I | ||
: never; | ||
declare const isOptional: unique symbol; | ||
declare class Optional { | ||
protected readonly [isOptional]: true; | ||
} | ||
type IfOptional<T extends Type, Then, Else> = T extends Optional ? Then : Else; | ||
abstract class Type<Output = unknown> { | ||
abstract class AbstractType<Output = unknown> { | ||
abstract readonly name: string; | ||
@@ -286,3 +314,3 @@ abstract genFunc(): Func<Output>; | ||
parse<T extends Type>( | ||
parse<T extends AbstractType>( | ||
this: T, | ||
@@ -309,17 +337,34 @@ v: unknown, | ||
optional(): Type<Output | undefined> & Optional { | ||
return new OptionalType(this) as OptionalType<Output> & Optional; | ||
try<T extends AbstractType>( | ||
this: T, | ||
v: unknown, | ||
options?: Partial<ParseOptions> | ||
): ValitaResult<Infer<T>> { | ||
let mode: FuncMode = FuncMode.PASS; | ||
if (options && options.mode === "strict") { | ||
mode = FuncMode.STRICT; | ||
} else if (options && options.mode === "strip") { | ||
mode = FuncMode.STRIP; | ||
} | ||
const r = this.func(v, mode); | ||
if (r === true) { | ||
return { ok: true, value: v as Infer<T> }; | ||
} else if (r.code === "ok") { | ||
return { ok: true, value: r.value as Infer<T> }; | ||
} else { | ||
return new ValitaFailure(r); | ||
} | ||
} | ||
default<T extends Literal>(defaultValue: T): DefaultOutput<Output, T>; | ||
default<T>(defaultValue: T): DefaultOutput<Output, T>; | ||
default<T, This extends this>( | ||
this: This, | ||
defaultValue: T | ||
): DefaultOutput<Output, T> { | ||
return this.optional().map((v) => | ||
v === undefined ? defaultValue : v | ||
) as unknown as DefaultOutput<Output, T>; | ||
optional(): Optional<Output> { | ||
return new Optional(this); | ||
} | ||
default<T extends Literal>(defaultValue: T): Default<Output, T>; | ||
default<T>(defaultValue: T): Default<Output, T>; | ||
default<T>(defaultValue: T): Default<Output, T> { | ||
return new Default(this, defaultValue); | ||
} | ||
assert<T extends Output>( | ||
@@ -356,6 +401,77 @@ func: ((v: Output) => v is T) | ((v: Output) => boolean), | ||
export type Infer<T extends Type> = T extends Type<infer I> ? I : never; | ||
declare const isOptional: unique symbol; | ||
type IfOptional<T extends AbstractType, Then, Else> = T extends Optional | ||
? Then | ||
: Else; | ||
type ObjectShape = Record<string, Type>; | ||
abstract class Type<Output = unknown> extends AbstractType<Output> { | ||
protected declare readonly [isOptional] = false; | ||
} | ||
class Optional<Output = unknown> extends AbstractType<Output | undefined> { | ||
protected declare readonly [isOptional] = true; | ||
readonly name = "optional"; | ||
constructor(private readonly type: AbstractType<Output>) { | ||
super(); | ||
} | ||
genFunc(): Func<Output | undefined> { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into: TerminalType[]): void { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class Default<Output, DefaultValue> extends Type< | ||
Exclude<Output, undefined> | DefaultValue | ||
> { | ||
readonly name = "default"; | ||
constructor( | ||
private readonly type: AbstractType<Output>, | ||
private readonly defaultValue: DefaultValue | ||
) { | ||
super(); | ||
} | ||
genFunc(): Func<Exclude<Output, undefined> | DefaultValue> { | ||
const func = this.type.func; | ||
const undefinedOutput: RawResult<DefaultValue> = | ||
this.defaultValue === undefined | ||
? true | ||
: { code: "ok", value: this.defaultValue }; | ||
const nothingOutput: RawResult<DefaultValue> = { | ||
code: "ok", | ||
value: this.defaultValue, | ||
}; | ||
return (v, mode) => { | ||
if (v === undefined) { | ||
return undefinedOutput; | ||
} else if (v === Nothing) { | ||
return nothingOutput; | ||
} else { | ||
const result = func(v, mode); | ||
if ( | ||
result !== true && | ||
result.code === "ok" && | ||
result.value === undefined | ||
) { | ||
return nothingOutput; | ||
} | ||
return result as RawResult<Exclude<Output, undefined>>; | ||
} | ||
}; | ||
} | ||
toTerminals(into: TerminalType[]): void { | ||
into.push(this.type.optional()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
type ObjectShape = Record<string, AbstractType>; | ||
type Optionals<T extends ObjectShape> = { | ||
@@ -367,3 +483,3 @@ [K in keyof T]: IfOptional<T[K], K, never>; | ||
T extends ObjectShape, | ||
R extends Type | undefined | ||
R extends AbstractType | undefined | ||
> = PrettyIntersection< | ||
@@ -376,3 +492,7 @@ { | ||
} & | ||
(R extends Type<infer I> ? { [K: string]: I } : unknown) | ||
(R extends Type<infer I> | ||
? { [K: string]: I } | ||
: R extends Optional<infer J> | ||
? Partial<{ [K: string]: J }> | ||
: unknown) | ||
>; | ||
@@ -382,3 +502,3 @@ | ||
Shape extends ObjectShape = ObjectShape, | ||
Rest extends Type | undefined = Type | undefined | ||
Rest extends AbstractType | undefined = AbstractType | undefined | ||
> extends Type<ObjectOutput<Shape, Rest>> { | ||
@@ -456,3 +576,3 @@ readonly name = "object"; | ||
if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} else if (!issueTree) { | ||
@@ -497,3 +617,3 @@ if (!copied) { | ||
} else if (r.code !== "ok") { | ||
issueTree = joinIssues(prependPath(key, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(key, r)); | ||
} else if (!issueTree) { | ||
@@ -553,15 +673,13 @@ if (!copied) { | ||
partial(): ObjectType< | ||
{ [K in keyof Shape]: OptionalType<Infer<Shape[K]>> & Optional }, | ||
Rest extends Type<infer I> ? OptionalType<I> & Optional : undefined | ||
{ [K in keyof Shape]: Optional<Infer<Shape[K]>> }, | ||
Rest extends AbstractType<infer I> ? Optional<I> : undefined | ||
> { | ||
const shape = {} as Record<string, unknown>; | ||
const rest = this.restType && this.restType.optional(); | ||
Object.keys(this.shape).forEach((key) => { | ||
shape[key] = this.shape[key].optional(); | ||
}); | ||
const rest = this.restType?.optional(); | ||
return new ObjectType( | ||
shape as { [K in keyof Shape]: OptionalType<Infer<Shape[K]>> & Optional }, | ||
rest as Rest extends Type<infer I> | ||
? OptionalType<I> & Optional | ||
: undefined | ||
shape as { [K in keyof Shape]: Optional<Infer<Shape[K]>> }, | ||
rest as Rest extends AbstractType<infer I> ? Optional<I> : undefined | ||
); | ||
@@ -628,3 +746,3 @@ } | ||
} else { | ||
issueTree = joinIssues(prependPath(i, r), issueTree); | ||
issueTree = joinIssues(issueTree, prependPath(i, r)); | ||
} | ||
@@ -685,5 +803,7 @@ } | ||
function createObjectMatchers(t: { root: Type; terminal: TerminalType }[]): { | ||
function createObjectMatchers( | ||
t: { root: AbstractType; terminal: TerminalType }[] | ||
): { | ||
key: string; | ||
optional?: Type; | ||
optional?: AbstractType; | ||
matcher: ( | ||
@@ -693,6 +813,6 @@ rootValue: unknown, | ||
mode: FuncMode | ||
) => Result<unknown>; | ||
) => RawResult<unknown>; | ||
}[] { | ||
const objects: { | ||
root: Type; | ||
root: AbstractType; | ||
terminal: TerminalType & { name: "object" }; | ||
@@ -769,3 +889,3 @@ }[] = []; | ||
); | ||
let optional: Type | undefined = undefined; | ||
let optional: AbstractType | undefined = undefined; | ||
for (let i = 0; i < flattened.length; i++) { | ||
@@ -787,10 +907,10 @@ const { root, terminal } = flattened[i]; | ||
function createUnionMatcher( | ||
t: { root: Type; terminal: TerminalType }[], | ||
t: { root: AbstractType; terminal: TerminalType }[], | ||
path?: Key[] | ||
): (rootValue: unknown, value: unknown, mode: FuncMode) => Result<unknown> { | ||
const order = new Map<Type, number>(); | ||
): (rootValue: unknown, value: unknown, mode: FuncMode) => RawResult<unknown> { | ||
const order = new Map<AbstractType, number>(); | ||
t.forEach(({ root }, i) => { | ||
order.set(root, order.get(root) ?? i); | ||
}); | ||
const byOrder = (a: Type, b: Type): number => { | ||
const byOrder = (a: AbstractType, b: AbstractType): number => { | ||
return (order.get(a) ?? 0) - (order.get(b) ?? 0); | ||
@@ -800,6 +920,6 @@ }; | ||
const expectedTypes = [] as BaseType[]; | ||
const literals = new Map<unknown, Type[]>(); | ||
const types = new Map<BaseType, Type[]>(); | ||
let unknowns = [] as Type[]; | ||
let optionals = [] as Type[]; | ||
const literals = new Map<unknown, AbstractType[]>(); | ||
const types = new Map<BaseType, AbstractType[]>(); | ||
let unknowns = [] as AbstractType[]; | ||
let optionals = [] as AbstractType[]; | ||
t.forEach(({ root, terminal }) => { | ||
@@ -868,3 +988,3 @@ if (terminal.name === "never") { | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -888,3 +1008,3 @@ } | ||
} | ||
issueTree = joinIssues(r, issueTree); | ||
issueTree = joinIssues(issueTree, r); | ||
count++; | ||
@@ -903,5 +1023,5 @@ } | ||
function flatten( | ||
t: { root: Type; type: Type }[] | ||
): { root: Type; terminal: TerminalType }[] { | ||
const result: { root: Type; terminal: TerminalType }[] = []; | ||
t: { root: AbstractType; type: AbstractType }[] | ||
): { root: AbstractType; terminal: TerminalType }[] { | ||
const result: { root: AbstractType; terminal: TerminalType }[] = []; | ||
t.forEach(({ root, type }) => | ||
@@ -939,3 +1059,3 @@ toTerminals(type).forEach((terminal) => { | ||
if (item.optional) { | ||
return item.optional.func(Nothing, mode) as Result< | ||
return item.optional.func(Nothing, mode) as RawResult< | ||
Infer<T[number]> | ||
@@ -946,7 +1066,11 @@ >; | ||
} | ||
return item.matcher(v, value, mode) as Result<Infer<T[number]>>; | ||
return item.matcher(v, value, mode) as RawResult<Infer<T[number]>>; | ||
} | ||
return base(v, v, mode) as Result<Infer<T[number]>>; | ||
return base(v, v, mode) as RawResult<Infer<T[number]>>; | ||
}; | ||
} | ||
optional(): Optional<Infer<T[number]>> { | ||
return new Optional(this); | ||
} | ||
} | ||
@@ -1047,23 +1171,6 @@ | ||
} | ||
class OptionalType<Output = unknown> extends Type<Output | undefined> { | ||
readonly name = "optional"; | ||
constructor(private readonly type: Type<Output>) { | ||
super(); | ||
} | ||
genFunc(): Func<Output> { | ||
const func = this.type.func; | ||
return (v, mode) => { | ||
return v === undefined || v === Nothing ? true : func(v, mode); | ||
}; | ||
} | ||
toTerminals(into: TerminalType[]): void { | ||
into.push(this); | ||
into.push(undefined_()); | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
class TransformType<Output> extends Type<Output> { | ||
readonly name = "transform"; | ||
constructor( | ||
protected readonly transformed: Type, | ||
protected readonly transformed: AbstractType, | ||
protected readonly transform: Func<unknown> | ||
@@ -1077,3 +1184,3 @@ ) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
let next: Type = this; | ||
let next: AbstractType = this; | ||
while (next instanceof TransformType) { | ||
@@ -1086,3 +1193,3 @@ chain.push(next.transform); | ||
const func = next.func; | ||
const undef = { code: "ok", value: undefined } as Result<unknown>; | ||
const undef = { code: "ok", value: undefined } as RawResult<unknown>; | ||
return (v, mode) => { | ||
@@ -1114,3 +1221,3 @@ let result = func(v, mode); | ||
} | ||
return result as Result<Output>; | ||
return result as RawResult<Output>; | ||
}; | ||
@@ -1122,2 +1229,22 @@ } | ||
} | ||
class LazyType<T> extends Type<T> { | ||
readonly name = "lazy"; | ||
constructor(private readonly definer: () => Type<T>) { | ||
super(); | ||
} | ||
private get type(): Type<T> { | ||
const type = this.definer(); | ||
Object.defineProperty(this, "type", { | ||
value: type, | ||
writable: false, | ||
}); | ||
return type; | ||
} | ||
genFunc(): Func<T> { | ||
return this.type.genFunc(); | ||
} | ||
toTerminals(into: TerminalType[]): void { | ||
this.type.toTerminals(into); | ||
} | ||
} | ||
@@ -1148,3 +1275,3 @@ function never(): NeverType { | ||
} | ||
function object<T extends Record<string, Type>>( | ||
function object<T extends Record<string, Type | Optional>>( | ||
obj: T | ||
@@ -1168,8 +1295,8 @@ ): ObjectType<T, undefined> { | ||
} | ||
function union<T extends Type[]>( | ||
...options: T | ||
): UnionType<T> & | ||
(true extends IfOptional<T[number], true, false> ? Optional : unknown) { | ||
return new UnionType(options) as UnionType<T> & Optional; | ||
function union<T extends Type[]>(...options: T): Type<Infer<T[number]>> { | ||
return new UnionType(options); | ||
} | ||
function lazy<T>(definer: () => Type<T>): Type<T> { | ||
return new LazyType(definer); | ||
} | ||
@@ -1188,3 +1315,3 @@ type TerminalType = | ||
| LiteralType | ||
| OptionalType; | ||
| Optional; | ||
@@ -1206,2 +1333,3 @@ export { | ||
undefined_ as undefined, | ||
lazy, | ||
ok, | ||
@@ -1211,2 +1339,2 @@ err, | ||
export type { Type }; | ||
export type { Type, Optional }; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
335536
5938