Files
suaveplan/packages/foundation/data/validation/src/schemas/tuple/tuple.ts
2026-05-27 23:47:53 +01:00

75 lines
2.4 KiB
TypeScript

import { addIssue, BaseSchema, INVALID } from "../../core/schema/schema.js";
import type { AnySchema, ParseContext } from "../../types/common.js";
// ---------------------------------------------------------------------------
// Type helpers
// ---------------------------------------------------------------------------
type TupleOutputs<T extends AnySchema[]> = {
[K in keyof T]: T[K] extends BaseSchema<unknown, infer O> ? O : never;
};
// ---------------------------------------------------------------------------
// TupleSchema
// ---------------------------------------------------------------------------
export class TupleSchema<T extends AnySchema[]> extends BaseSchema<
TupleOutputs<T>,
TupleOutputs<T>
> {
private readonly _schemas: T;
constructor(schemas: T) {
super();
this._schemas = schemas;
}
_parse(input: unknown, ctx: ParseContext): TupleOutputs<T> | INVALID {
if (!Array.isArray(input)) {
return addIssue(ctx, {
code: "VALIDATION.INVALID_TYPE",
path: [...ctx.path],
message: `Expected tuple (array), received ${typeof input}`,
input,
});
}
if (input.length !== this._schemas.length) {
return addIssue(ctx, {
code: "VALIDATION.INVALID_TYPE",
path: [...ctx.path],
message: `Expected tuple of length ${this._schemas.length}, received length ${input.length}`,
input,
});
}
const out: unknown[] = [];
let hasInvalid = false;
for (let i = 0; i < this._schemas.length; i++) {
const schema = this._schemas[i]!;
const childCtx: ParseContext = {
path: [...ctx.path, i],
issues: [],
};
const result = schema._parse(input[i], childCtx);
if (result === INVALID || childCtx.issues.length > 0) {
ctx.issues.push(...childCtx.issues);
hasInvalid = true;
} else {
out.push(result);
}
}
return hasInvalid ? INVALID : (out as TupleOutputs<T>);
}
}
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
export function tuple<T extends AnySchema[]>(schemas: T): TupleSchema<T> {
return new TupleSchema(schemas);
}