typia
Version:
Superfast runtime validators with only one line
100 lines (92 loc) • 2.66 kB
text/typescript
import ts from "typescript";
import { MetadataObject } from "../../schemas/metadata/MetadataObject";
import { FeatureProgrammer } from "../FeatureProgrammer";
/**
* @internal
*/
export const decode_union_object =
(
checker: (
input: ts.Expression,
obj: MetadataObject,
explore: FeatureProgrammer.IExplore,
) => ts.Expression,
) =>
(
decoder: (
input: ts.Expression,
obj: MetadataObject,
explore: FeatureProgrammer.IExplore,
) => ts.Expression,
) =>
(success: (exp: ts.Expression) => ts.Expression) =>
(escaper: (value: ts.Expression, expected: string) => ts.Statement) =>
(
input: ts.Expression,
targets: MetadataObject[],
explore: FeatureProgrammer.IExplore,
): ts.CallExpression =>
ts.factory.createCallExpression(
ts.factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
undefined,
iterate(escaper)(
input,
targets.map((obj) => ({
type: "object",
is: () => success(checker(input, obj, explore)),
value: () => decoder(input, obj, explore),
})),
`(${targets.map((t) => t.name).join(" | ")})`,
),
),
undefined,
undefined,
);
const iterate =
(escaper: (value: ts.Expression, expected: string) => ts.Statement) =>
(input: ts.Expression, unions: IUnion[], expected: string) => {
interface IBranch {
condition: null | ts.Expression;
value: ts.Expression;
}
const branches: IBranch[] = [];
for (const u of unions) {
const condition: ts.Expression = u.is();
if (condition.kind === ts.SyntaxKind.TrueKeyword) {
branches.push({
condition: null,
value: u.value(),
});
break;
}
branches.push({
condition,
value: u.value(),
});
}
if (branches.length === 0)
return ts.factory.createBlock([escaper(input, expected)], true);
else if (branches.length === 1 && branches[0]!.condition === null)
return branches[0]!.value;
const statements: ts.Statement[] = branches.map((b) =>
b.condition !== null
? ts.factory.createIfStatement(
b.condition,
ts.factory.createReturnStatement(b.value),
undefined,
)
: ts.factory.createReturnStatement(b.value),
);
if (branches.at(-1)!.condition !== null)
statements.push(escaper(input, expected));
return ts.factory.createBlock(statements, true);
};
interface IUnion {
type: string;
is: () => ts.Expression;
value: () => ts.Expression;
}