typeparse
Version:
Runtime object transformation, parsing and validation with inferred static TypeScript typing.
324 lines (323 loc) • 10.8 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Kenneth Herrera. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Types = exports.TypeParse = void 0;
const Error_1 = require("./Error");
const Utils_1 = require("./Utils");
class TypeParse {
constructor(parseConfig) {
this.config = parseConfig;
}
parse(input, config) {
try {
const parsed = this._parse(input, this.config);
if (config === null || config === void 0 ? void 0 : config.safeParse)
return {
success: true,
data: parsed,
};
return parsed;
}
catch (error) {
if (config === null || config === void 0 ? void 0 : config.safeParse) {
return {
success: false,
error: error.toString(),
};
}
throw error;
}
}
_parse(input, config, relativePath) {
switch (config.as) {
case "object":
return this.parseObject(input, config, relativePath);
case "array":
return this.parseArray(input, config, relativePath);
case "string":
return this.parseString(input, config, relativePath);
case "number":
return this.parseNumber(input, config, relativePath);
case "boolean":
return this.parseBoolean(input, config, relativePath);
case "union":
return this.parseUnion(input, config, relativePath);
case "custom":
return this.parseCustom(input, config, relativePath);
default:
return this.parseAny(input, config, relativePath);
}
}
parseString(input, config, relativePath) {
var _a;
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
if (typeof input === "string")
return input;
if (typeof input === "number" || typeof input === "boolean")
return `${input}`;
if (config.defaultValue !== undefined) {
return config.defaultValue;
}
else if (config.isOptional) {
return undefined;
}
else {
throw new Error_1.TypeParseError({
expectedType: "string",
typeFound: input,
key: (_a = config.from) !== null && _a !== void 0 ? _a : relativePath,
});
}
}
parseNumber(input, config, relativePath) {
var _a;
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
if (typeof input === "number")
return input;
if (typeof input === "string") {
const result = Number.parseFloat(input);
if (!isNaN(result)) {
return result;
}
}
if (config.defaultValue !== undefined) {
return config.defaultValue;
}
else if (config.isOptional) {
return undefined;
}
else {
throw new Error_1.TypeParseError({
expectedType: "number",
typeFound: input,
key: (_a = config.from) !== null && _a !== void 0 ? _a : relativePath,
});
}
}
parseBoolean(input, config, relativePath) {
var _a;
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
if (typeof input === "boolean")
return input;
if (!config.strict && typeof input !== "undefined")
return !!input;
if (config.defaultValue !== undefined) {
return config.defaultValue;
}
else if (config.isOptional) {
return undefined;
}
else {
throw new Error_1.TypeParseError({
expectedType: "boolean",
typeFound: input,
key: (_a = config.from) !== null && _a !== void 0 ? _a : relativePath,
});
}
}
parseAny(input, config, relativePath) {
var _a;
if (typeof input !== "object")
return input;
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
return (_a = input !== null && input !== void 0 ? input : config.defaultValue) !== null && _a !== void 0 ? _a : input;
}
parseCustom(input, config, relativePath) {
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
try {
return config.parseFunction(input);
}
catch (error) {
if (config.isOptional) {
return undefined;
}
else {
throw new Error_1.TypeParseError({
expectedType: "unknown",
customError: error,
});
}
}
}
parseArray(input, config, relativePath) {
var _a;
if (config.from)
input = (0, Utils_1.get)(input, config.from);
else
input = (0, Utils_1.get)(input, relativePath);
if (!Array.isArray(input)) {
if (config.isOptional)
return undefined;
throw new Error_1.TypeParseError({
expectedType: "array",
typeFound: input,
key: (_a = config.from) !== null && _a !== void 0 ? _a : relativePath,
});
}
const results = [];
input.forEach((element) => {
const parsed = this._parse(element, config.type);
if (parsed !== undefined)
results.push(parsed);
});
return results;
}
parseObject(input, config, relativePath) {
if (config.from)
relativePath = config.from;
if (typeof input !== "object") {
if (config.isOptional)
return undefined;
throw new Error_1.TypeParseError({
expectedType: "object",
typeFound: input,
key: relativePath,
});
}
const result = {};
try {
Object.entries(config.properties).forEach(([key, value]) => {
result[key] = this._parse(input, value, relativePath ? `${relativePath}.${key}` : key);
});
}
catch (error) {
if (config.isOptional)
return undefined;
throw error;
}
return result;
}
parseUnion(input, config, relativePath) {
for (const value of config.types) {
try {
return this._parse(input, value, relativePath);
}
catch (_a) {
continue;
}
}
if (config.isOptional) {
return undefined;
}
else {
throw new Error("Cannot parse to match any type of union");
}
}
}
exports.TypeParse = TypeParse;
const optional = (option) => {
return Object.assign(Object.assign({}, option), { isOptional: true, optional: undefined });
};
exports.Types = {
String: (config) => {
return {
$static: undefined,
as: "string",
defaultValue: config === null || config === void 0 ? void 0 : config.defaultValue,
isOptional: false,
optional: function () {
return optional(this);
},
from: config === null || config === void 0 ? void 0 : config.path,
};
},
Number: (config) => {
return {
$static: undefined,
as: "number",
defaultValue: config === null || config === void 0 ? void 0 : config.defaultValue,
isOptional: false,
optional: function () {
return optional(this);
},
from: config === null || config === void 0 ? void 0 : config.path,
};
},
Boolean: (config) => {
return {
$static: undefined,
as: "boolean",
defaultValue: config === null || config === void 0 ? void 0 : config.defaultValue,
strict: (config === null || config === void 0 ? void 0 : config.strict) !== undefined ? config.strict : true,
isOptional: false,
optional: function () {
return optional(this);
},
from: config === null || config === void 0 ? void 0 : config.path,
};
},
Any: (config) => {
return {
$static: undefined,
as: "any",
isOptional: false,
from: config === null || config === void 0 ? void 0 : config.path,
defaultValue: config === null || config === void 0 ? void 0 : config.defaultValue,
};
},
Array: (type, config) => {
return {
$static: undefined,
as: "array",
isOptional: false,
optional: function () {
return optional(this);
},
from: config === null || config === void 0 ? void 0 : config.path,
type,
};
},
Object: (properties, config) => {
return {
$static: undefined,
as: "object",
isOptional: false,
optional: function () {
return optional(this);
},
properties,
from: config === null || config === void 0 ? void 0 : config.path,
};
},
Union: (types) => {
return {
$static: undefined,
as: "union",
isOptional: false,
optional: function () {
return optional(this);
},
types,
};
},
Custom: (func, config) => {
return {
$static: undefined,
as: "custom",
isOptional: false,
optional: function () {
return optional(this);
},
parseFunction: func,
from: config === null || config === void 0 ? void 0 : config.path,
};
},
};