UNPKG

typia

Version:

Superfast runtime validators with only one line

175 lines (162 loc) • 5.22 kB
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));