typia
Version:
Superfast runtime validators with only one line
151 lines (137 loc) • 4.71 kB
text/typescript
import ts from "typescript";
import { RandomGenerator } from "../utils/RandomGenerator";
export namespace ExpressionFactory {
export const number = (value: number) =>
value < 0
? ts.factory.createPrefixUnaryExpression(
ts.SyntaxKind.MinusToken,
ts.factory.createNumericLiteral(Math.abs(value)),
)
: ts.factory.createNumericLiteral(value);
export const bigint = (value: number | bigint) =>
ts.factory.createCallExpression(
ts.factory.createIdentifier("BigInt"),
undefined,
[ts.factory.createIdentifier(value.toString())],
);
export const isRequired = (input: ts.Expression): ts.Expression =>
ts.factory.createStrictInequality(
ts.factory.createIdentifier("undefined"),
input,
);
export const isArray = (input: ts.Expression): ts.Expression =>
ts.factory.createCallExpression(
ts.factory.createIdentifier("Array.isArray"),
undefined,
[input],
);
export const isObject =
(options: { checkNull: boolean; checkArray: boolean }) =>
(input: ts.Expression): ts.Expression => {
const conditions: ts.Expression[] = [
ts.factory.createStrictEquality(
ts.factory.createStringLiteral("object"),
ts.factory.createTypeOfExpression(input),
),
];
if (options.checkNull === true)
conditions.push(
ts.factory.createStrictInequality(ts.factory.createNull(), input),
);
if (options.checkArray === true)
conditions.push(
ts.factory.createStrictEquality(
ts.factory.createFalse(),
ts.factory.createCallExpression(
ts.factory.createIdentifier("Array.isArray"),
undefined,
[input],
),
),
);
return conditions.length === 1
? conditions[0]!
: conditions.reduce((x, y) => ts.factory.createLogicalAnd(x, y));
};
export const isInstanceOf =
(type: string) =>
(input: ts.Expression): ts.Expression => {
return ts.factory.createBinaryExpression(
input,
ts.factory.createToken(ts.SyntaxKind.InstanceOfKeyword),
ts.factory.createIdentifier(type),
);
};
export const coalesce =
(x: ts.Expression) =>
(y: ts.Expression): ts.Expression =>
ts.factory.createBinaryExpression(
x,
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
y,
);
export const currying =
(target: ts.Expression) => (parameters: ts.Expression[]) => {
if (parameters.length === 0)
return ts.factory.createCallExpression(target, undefined, undefined);
let prev: ts.CallExpression = ts.factory.createCallExpression(
target,
undefined,
[parameters[0]!],
);
for (const param of parameters.slice(1))
prev = ts.factory.createCallExpression(prev, undefined, [param]);
return prev;
};
export const selfCall = (body: ts.ConciseBody) =>
ts.isCallExpression(body)
? body
: ts.factory.createCallExpression(
ts.factory.createParenthesizedExpression(
ts.factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
undefined,
body,
),
),
undefined,
undefined,
);
export const getEscapedText =
(printer: ts.Printer) =>
(input: ts.Expression): string =>
printer.printNode(ts.EmitHint.Expression, input, input.getSourceFile());
export const transpile =
(context: ts.TransformationContext) => (script: string) => {
const file: ts.SourceFile = ts.createSourceFile(
`${RandomGenerator.uuid()}.ts`,
script,
ts.ScriptTarget.ESNext,
true,
ts.ScriptKind.TS,
);
const statement: ts.Statement | undefined = file.statements[0];
if (statement === undefined)
throw new ReferenceError(
"Error on ExpressionFactory.transpile(): no statement exists.",
);
else if (!ts.isExpressionStatement(statement))
throw new TypeError(
"Error on ExpressionFactory.transpile(): statement is not an expression statement.",
);
return (input: ts.Expression): ts.Expression => {
const visitor = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node) && node.text === "$input") return input;
return ts.visitEachChild(
(ts.factory as any).cloneNode(node),
visitor,
context,
);
};
return visitor(statement.expression) as ts.Expression;
};
};
}