diff --git a/src/gen/nodes/index.ts b/src/gen/nodes/index.ts index 5c89a77..5f6abb9 100644 --- a/src/gen/nodes/index.ts +++ b/src/gen/nodes/index.ts @@ -35,6 +35,7 @@ import { } from "../expressionUtils"; import {CodeReferenceKind, Transformer} from "../../transformer"; import {TransformerError, genCheckCtx, isSingleIfStatement} from "../../utils"; +import { getUnionMembers } from "../../utils/unions"; export interface ValidationResultType { throw?: string | ts.Symbol; @@ -484,38 +485,6 @@ export function isNullableNode(validator: Validator): ts.Expression { return _bin(validator.expression(), UNDEFINED, ts.SyntaxKind.ExclamationEqualsEqualsToken); } -export function getUnionMembers(validators: Validator[]): { - compound: Validator[]; - normal: Validator[]; - object: [Validator, Validator][]; - isNullable: boolean; -} { - const compoundTypes: Validator[] = [], - normalTypes: Validator[] = [], - objectTypes: [Validator, Validator][] = []; - let isNullable = false; - const objectKind = validators.filter(v => v.typeData.kind === TypeDataKinds.Object).length; - for (const child of validators) { - if (child.typeData.kind === TypeDataKinds.Undefined) { - isNullable = true; - continue; - } else if (child.children.length && child.typeData.kind === TypeDataKinds.Object && objectKind > 1) { - const idRepresent = child.getFirstLiteralChild(); - if (idRepresent) { - child.children.splice(child.children.indexOf(idRepresent), 1); - objectTypes.push([child, idRepresent]); - } else compoundTypes.push(child); - } else if (child.isComplexType()) compoundTypes.push(child); - else normalTypes.push(child); - } - return { - compound: compoundTypes, - normal: normalTypes, - object: objectTypes, - isNullable - }; -} - export function genStatements(results: GenResult[], ctx: NodeGenContext): ts.Statement[] { const result = []; for (const genResult of results) { diff --git a/src/gen/nodes/match.ts b/src/gen/nodes/match.ts index 1394715..ff0d419 100644 --- a/src/gen/nodes/match.ts +++ b/src/gen/nodes/match.ts @@ -3,8 +3,9 @@ import ts from "typescript"; import {Transformer} from "../../transformer"; import {TypeDataKinds, Validator, genValidator} from "../validators"; import {UNDEFINED, _access, _and, _arr_check, _arrow_fn, _bin, _bool, _call, _ident, _not, _obj_check, _or, BlockLike, _if_chain, _var} from "../expressionUtils"; -import {GenResult, NodeGenContext, createContext, genChecks, genNode, getUnionMembers} from "./"; +import {GenResult, NodeGenContext, createContext, genChecks, genNode} from "./"; import {doesAlwaysReturn} from "../../utils"; +import { getUnionMembers } from "../../utils/unions"; export interface MatchArm { parameter: ts.ParameterDeclaration; diff --git a/src/gen/nodes/transform.ts b/src/gen/nodes/transform.ts index 132d3e5..cf3660a 100644 --- a/src/gen/nodes/transform.ts +++ b/src/gen/nodes/transform.ts @@ -1,7 +1,9 @@ +import {createContext, genNode} from "."; import {Transformer} from "../../transformer"; import {genCheckCtx} from "../../utils"; -import {_access, _assign, _call, _for, _ident, _obj, _stmt, _var} from "../expressionUtils"; -import {TypeDataKinds, Validator} from "../validators"; +import {getUnionMembers} from "../../utils/unions"; +import {_access, _assign, _call, _for, _ident, _if_chain, _not, _obj, _stmt, _var} from "../expressionUtils"; +import {TransformTypeData, TypeDataKinds, Validator} from "../validators"; import ts from "typescript"; export interface TransformCtx { @@ -10,11 +12,11 @@ export interface TransformCtx { } export function genTransform(validator: Validator, target: ts.Expression, ctx: TransformCtx): ts.Statement[] { - const assignTarget = validator.parent ? _access(target, validator.name) : target; + const assignTarget = validator.parent && validator.name !== "" ? _access(target, validator.name) : target; switch (validator.typeData.kind) { case TypeDataKinds.Transform: { - if (!validator.typeData.transformations) return [_stmt(ts.factory.createNull())]; + if (!validator.typeData.transformations.length) return []; const prevStmts = []; let previousExp = validator.expression(); for (let i = 0; i < validator.typeData.transformations.length; i++) { @@ -52,6 +54,27 @@ export function genTransform(validator: Validator, target: ts.Expression, ctx: T } return [_stmt(_assign(assignTarget, ts.factory.createArrayLiteralExpression())), _for(validator.expression(), index, genTransform(childType, assignTarget, ctx))[0]]; } + case TypeDataKinds.Union: { + const transforms = validator.getChildrenOfKind(TypeDataKinds.Transform); + if (!transforms.length) return [_stmt(_assign(assignTarget, validator.expression()))]; + const transformBases = transforms.map(transform => (transform.typeData as TransformTypeData).rest).filter(i => i) as Validator[]; + + const { normal, compound } = getUnionMembers(transformBases); + + const nodeCtx = createContext(ctx.transformer, {none: true}, ctx.origin); + + return [ + _if_chain( + 0, + [...normal, ...compound].map(validator => { + const check = genNode(validator, nodeCtx); + const originalTransform = transforms[transformBases.indexOf(validator)] as Validator; + + return [_not(check.condition), genTransform(originalTransform, assignTarget, ctx)]; + }) + ) as ts.Statement + ]; + } default: return [_stmt(_assign(assignTarget, validator.expression()))]; } diff --git a/src/gen/validators/genValidator.ts b/src/gen/validators/genValidator.ts index 10cc12f..62a550c 100644 --- a/src/gen/validators/genValidator.ts +++ b/src/gen/validators/genValidator.ts @@ -43,7 +43,8 @@ export function genValidator(transformer: Transformer, type: ts.Type | undefined else return; } else if (type.getCallSignatures().length === 1) return new Validator(type, name, {kind: TypeDataKinds.Function}, exp, parent); else { - if (type.getProperty("__$transform")) { + if (type.isUnion()) return createUnionValidator(type, type.types, transformer, name, exp, parent); + else if (type.getProperty("__$transform")) { const expressions: CodeReference[] = []; let rest: Validator | undefined; @@ -108,7 +109,6 @@ export function genValidator(transformer: Transformer, type: ts.Type | undefined } const utility = transformer.getPropType(type, "name"); if (!utility || !utility.isStringLiteral()) { - if (type.isUnion()) return createUnionValidator(type, type.types, transformer, name, exp, parent); const properties = (parent: Validator) => type.getProperties().map(sym => { const typeOfProp = (transformer.checker.getTypeOfSymbol(sym) || transformer.checker.getNullType()) as ts.Type; diff --git a/src/gen/validators/validator.ts b/src/gen/validators/validator.ts index 93c6cce..b37f3eb 100644 --- a/src/gen/validators/validator.ts +++ b/src/gen/validators/validator.ts @@ -277,15 +277,6 @@ export class Validator { return result; } - areChildrenSameKind(): boolean { - if (!this.children.length) return true; - const firstChildKind = (this.children[0] as Validator).typeData.kind; - for (let i = 1; i < this.children.length; i++) { - if ((this.children[i] as Validator).typeData.kind !== firstChildKind) return false; - } - return true; - } - getFirstLiteralChild(): Validator | undefined { for (const child of this.children) { if ((child.typeData.kind === TypeDataKinds.String && child.typeData.literal !== undefined) || (child.typeData.kind === TypeDataKinds.Number && child.typeData.literal !== undefined)) diff --git a/src/utils/unions.ts b/src/utils/unions.ts new file mode 100644 index 0000000..a036dc7 --- /dev/null +++ b/src/utils/unions.ts @@ -0,0 +1,33 @@ +import { Validator, TypeDataKinds } from "../gen/validators"; + +export function getUnionMembers(validators: Validator[]): { + compound: Validator[]; + normal: Validator[]; + object: [Validator, Validator][]; + isNullable: boolean; +} { + const compoundTypes: Validator[] = [], + normalTypes: Validator[] = [], + objectTypes: [Validator, Validator][] = []; + let isNullable = false; + const objectKind = validators.filter(v => v.typeData.kind === TypeDataKinds.Object).length; + for (const child of validators) { + if (child.typeData.kind === TypeDataKinds.Undefined) { + isNullable = true; + continue; + } else if (child.children.length && child.typeData.kind === TypeDataKinds.Object && objectKind > 1) { + const idRepresent = child.getFirstLiteralChild(); + if (idRepresent) { + child.children.splice(child.children.indexOf(idRepresent), 1); + objectTypes.push([child, idRepresent]); + } else compoundTypes.push(child); + } else if (child.isComplexType()) compoundTypes.push(child); + else normalTypes.push(child); + } + return { + compound: compoundTypes, + normal: normalTypes, + object: objectTypes, + isNullable + }; +} \ No newline at end of file