typia
Version:
Superfast runtime validators with only one line
175 lines (162 loc) • 5.22 kB
text/typescript
import ts from "typescript";
import { Metadata } from "../../schemas/metadata/Metadata";
import { IProject } from "../../transformers/IProject";
import { FunctionImporter } from "../helpers/FunctionImporter";
import { ICheckEntry } from "../helpers/ICheckEntry";
import { check_bigint } from "./check_bigint";
import { check_number } from "./check_number";
import { check_string } from "./check_string";
import { check_template } from "./check_template";
export const check_dynamic_key =
(project: IProject) =>
(importer: FunctionImporter) =>
(input: ts.Expression, metadata: Metadata): ts.Expression => {
// IF PURE STRING EXISTS, THEN SKIP VALIDATION
if (
(metadata.atomics.length !== 0 &&
metadata.atomics.some(
(a) =>
a.type === "string" &&
a.tags.filter((row) => row.every((t) => t.validate !== undefined))
.length === 0,
)) ||
(metadata.natives.length !== 0 &&
metadata.natives.some((type) => type === "String"))
)
return ts.factory.createTrue();
const conditions: ts.Expression[] = [];
// NULLISH COALESCING
if (metadata.nullable === true)
conditions.push(
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("null"),
input,
),
);
if (metadata.isRequired() === false)
conditions.push(
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("undefined"),
input,
),
);
// ATOMICS
for (const atom of metadata.atomics)
if (atom.type === "boolean")
conditions.push(
ts.factory.createLogicalOr(
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("false"),
input,
),
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("true"),
input,
),
),
);
else if (atom.type === "bigint")
conditions.push(
ts.factory.createLogicalAnd(
ts.factory.createCallExpression(
importer.use("is_bigint_string"),
undefined,
[input],
),
atomist(
check_bigint(project)(atom)(
ts.factory.createCallExpression(
ts.factory.createIdentifier("BigInt"),
undefined,
[input],
),
),
),
),
);
else if (atom.type === "number")
conditions.push(
atomist(
check_number(project, true)(atom)(
ts.factory.createCallExpression(
ts.factory.createIdentifier("Number"),
undefined,
[input],
),
),
),
);
else conditions.push(atomist(check_string(project)(atom)(input)));
// CONSTANTS
for (const constant of metadata.constants)
for (const { value } of constant.values)
conditions.push(
ts.factory.createStrictEquality(
ts.factory.createStringLiteral(String(value)),
input,
),
);
// TEMPLATES
if (!!metadata.templates.length)
conditions.push(atomist(check_template(metadata.templates)(input)));
// NATIVES
for (const native of metadata.natives)
if (native === "Boolean")
conditions.push(
ts.factory.createLogicalOr(
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("false"),
input,
),
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("true"),
input,
),
),
);
else if (native === "BigInt")
conditions.push(
ts.factory.createCallExpression(
importer.use("is_bigint_string"),
undefined,
[input],
),
);
else if (native === "Number")
conditions.push(
ts.factory.createStrictEquality(
ts.factory.createFalse(),
ts.factory.createCallExpression(
ts.factory.createIdentifier("Number.isNaN"),
undefined,
[
ts.factory.createCallExpression(
ts.factory.createIdentifier("Number"),
undefined,
[input],
),
],
),
),
);
return conditions.length === 0
? ts.factory.createTrue()
: conditions.length === 1
? conditions[0]!
: conditions.reduce(ts.factory.createLogicalOr);
};
const atomist = (entry: ICheckEntry) =>
[
...(entry.expression ? [entry.expression] : []),
...(entry.conditions.length === 0
? []
: [
entry.conditions
.map((set) =>
set
.map((s) => s.expression)
.reduce((a, b) => ts.factory.createLogicalAnd(a, b)),
)
.reduce((a, b) => ts.factory.createLogicalOr(a, b)),
]),
].reduce((x, y) => ts.factory.createLogicalAnd(x, y));