@pcvl/schema / ArrayInterpretedSchema
Class: ArrayInterpretedSchema<ItemSchema, Input, Output>
Defined in: schemas/arrayInterpretedSchema.ts:78
Validates every element against one shared item schema. Build one with pvl.array(item).
Elements are independent: every element is checked even after an earlier one fails, so a single Result carries one Issue per failing element, each pathed with its numeric index. Nest arrays and objects freely — each level appends its own path segment, so a failure deep inside reports exactly where it happened.
The length constraints are checks on the array itself. They run, in the order chained, only once every element has passed, so they never report alongside element issues; between themselves they all report rather than stopping at the first. There is no .coerce(): there is no unambiguous way to read an array out of a non-array.
Every modifier hands back an array schema rather than the base ChainableInterpretedSchema, so a modified array schema is still something pvl.compile() accepts and the length constraints stay chainable. .transform() ends the chain.
Example
import { pvl } from '@pcvl/schema';
const tags = pvl.array(pvl.string()).min(1).max(5);
tags.validate(['a', 'b']); // { value: ['a', 'b'] }
tags.validate(['a', 2, 3]);
// { issues: [{ path: [1], ... }, { path: [2], ... }] } — every failing elementExtends
ChainableInterpretedSchema<Input,Output>
Type Parameters
ItemSchema
ItemSchema extends Schema = Schema
Input
Input = InferInput<ItemSchema>[]
Output
Output = InferOutput<ItemSchema>[]
Accessors
element
Get Signature
get element(): StandaloneElement<ItemSchema>;Defined in: schemas/arrayInterpretedSchema.ts:123
The schema every element of this array is validated against, when the item schema is marked .standalone(), so a single item can be reached and validated on its own without validating a whole array, and undefined otherwise. An object item carries its own shape, holding its Standalone Keys, and a marked array item its own element, so nested structure is reachable all the way down. It survives every modifier, .standalone() and pvl.compile(), but not .transform(), whose value needn't be an array.
Read-only: reading the item schema never affects how this schema validates, and it cannot be replaced.
Example
import { pvl } from '@pcvl/schema';
const users = pvl.array(pvl.object({ name: pvl.string().standalone() }).standalone());
users.element.validate({ name: 'Ada' }); // { value: { name: 'Ada' } }
users.element.shape.name.validate(42); // { issues: [{ code: 'INVALID_TYPE', ... }] }
pvl.array(pvl.string()).element; // undefined: the item isn't markedReturns
StandaloneElement<ItemSchema>
Methods
length()
length(exactLength, options?): this;Defined in: schemas/arrayInterpretedSchema.ts:203
Requires exactly exactLength elements.
Parameters
exactLength
number
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const rgb = pvl.array(pvl.number().int().min(0).max(255)).length(3);
rgb.validate([12, 34, 56]); // { value: [12, 34, 56] }
rgb.validate([12, 34]); // { issues: [{ code: 'INVALID_LENGTH', ... }] }max()
max(maxLength, options?): this;Defined in: schemas/arrayInterpretedSchema.ts:172
Requires at most maxLength elements.
Parameters
maxLength
number
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const tags = pvl.array(pvl.string()).max(5);
tags.validate(['a', 'b', 'c', 'd', 'e', 'f']); // { issues: [{ code: 'TOO_BIG', ... }] }min()
min(minLength, options?): this;Defined in: schemas/arrayInterpretedSchema.ts:142
Requires at least minLength elements.
Parameters
minLength
number
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const tags = pvl.array(pvl.string()).min(1, { message: 'pick at least one tag' });
tags.validate([]); // { issues: [{ code: 'TOO_SMALL', message: 'pick at least one tag' }] }nullable()
nullable(): ArrayInterpretedSchema<ItemSchema, Input | null, Output | null>;Defined in: schemas/chainableInterpretedSchema.ts:78
Accepts null in addition to whatever this schema already accepts. Unlike .optional(), the key stays required — null has to be passed explicitly. Chain both to accept either.
Returns
ArrayInterpretedSchema<ItemSchema, Input | null, Output | null>
Example
import { pvl } from '@pcvl/schema';
const deletedAt = pvl.string().nullable();
deletedAt.validate(null); // { value: null }
const eitherWay = pvl.string().nullable().optional();Inherited from
ChainableInterpretedSchema.nullable
optional()
optional(): ArrayInterpretedSchema<ItemSchema, Input | undefined, Output | undefined>;Defined in: schemas/chainableInterpretedSchema.ts:54
Accepts undefined in addition to whatever this schema already accepts. As an object field, it also makes the key itself optional: an omitted key stays omitted from the output rather than becoming an explicit undefined property.
Returns
ArrayInterpretedSchema<ItemSchema, Input | undefined, Output | undefined>
Example
import { pvl } from '@pcvl/schema';
const user = pvl.object({ name: pvl.string(), nickname: pvl.string().optional() });
user.validate({ name: 'Ada' }); // { value: { name: 'Ada' } }
user.validate({ name: 'Ada', nickname: 'Addie' }); // both keys keptInherited from
ChainableInterpretedSchema.optional
refine()
refine<Refined>(predicate, options?): ArrayInterpretedSchema<ItemSchema, Input, RefinedOutput<Output, Refined>>;Defined in: schemas/chainableInterpretedSchema.ts:134
Attaches a custom check. The predicate never changes the value; returning false produces an Issue with code CUSTOM. This is where constraints the library has no built-in for — a regex, a cross-field rule — belong.
It runs once the value has passed this schema's type check, in the order it was chained among the constraints, so a failing constraint before it does not stop it and both issues are reported. It is skipped for a value .optional() or .nullable() accepted.
When the predicate is a type predicate, written out ((value): value is T) or inferred by TypeScript ((value) => value === 'ahoj'), the output narrows to its type; a predicate returning a plain boolean leaves it as it was. The input never narrows. A null or undefined that .nullable() or .optional() chained before lets past the predicate stays in the output.
The schema comes back as the same type it went in as, so a refined object or array schema is still one, with its shape or element.
Type Parameters
Refined
Refined = Output
Parameters
predicate
((value) => boolean) | ((value) => value is Refined)
options?
Partial<Pick<IssueProps, "message">>
Returns
ArrayInterpretedSchema<ItemSchema, Input, RefinedOutput<Output, Refined>>
Example
import { pvl } from '@pcvl/schema';
const evenNumber = pvl
.number()
.refine((value) => value % 2 === 0, { message: 'must be even' });
evenNumber.validate(3); // { issues: [{ code: 'CUSTOM', message: 'must be even' }] }
// Output 'ahoj', input still string.
const greeting = pvl.string().refine((value) => value === 'ahoj');
// Output string | null: a null never reaches the predicate.
const name = pvl
.string()
.nullable()
.refine((value) => value !== null);
// Still an object schema, so `shape` is still there.
const range = pvl
.object({ min: pvl.number().standalone(), max: pvl.number() })
.refine((value) => value.min <= value.max);
range.shape.min.validate(1); // { value: 1 }Inherited from
ChainableInterpretedSchema.refine
standalone()
standalone(): ArrayInterpretedSchema<ItemSchema, Input, Output> & StandaloneMarker;Defined in: schemas/interpretedSchema.ts:119
Marks this schema as a Standalone Key: as a field of pvl.object(), it is kept in that object's shape, so it can be validated on its own, and once the object is compiled, @pcvl/compiler gives it a compiled validator of its own. Validation is unchanged.
The schema comes back as the same type it went in as, marked, so the chain goes on: modifiers can follow it, and an object keeps its shape and an array its element. It is valid on any schema, a field or not, and calling it again changes nothing.
Returns
ArrayInterpretedSchema<ItemSchema, Input, Output> & StandaloneMarker
Example
import { pvl } from '@pcvl/schema';
const user = pvl.object({
email: pvl.string().standalone().min(3),
age: pvl.number().int(),
});
user.shape.email.validate('a@b.c'); // { value: 'a@b.c' }
// user.shape.age — a compile error: `age` isn't a Standalone KeyInherited from
ChainableInterpretedSchema.standalone
transform()
transform<NewOutput>(fn): Schema<Input, NewOutput>;Defined in: schemas/chainableInterpretedSchema.ts:176
Converts an accepted value into a different one, changing the Output type to whatever the function returns. It is the last thing to run, and only once nothing has failed, so the function only ever sees a value this schema accepted — unlike .coerce(), which runs before the type check. A value .optional() or .nullable() accepted still reaches it, so the function is typed to receive undefined or null too.
It ends the chain: the result is a plain Schema, which can validate but takes no further modifier, so chain those before transforming. A transformed object has no shape, and a transformed array no element: they describe a value the function replaces.
Type Parameters
NewOutput
NewOutput
Parameters
fn
(value) => NewOutput
Returns
Schema<Input, NewOutput>
Example
import { pvl } from '@pcvl/schema';
const trimmedLength = pvl.string().transform((value) => value.trim().length);
trimmedLength.validate(' hello '); // { value: 5 }
trimmedLength.validate(42); // { issues: [...] } — never reaches the transform
const label = pvl.string().nullable().transform((value) => value ?? 'none');
label.validate(null); // { value: 'none' }Inherited from
ChainableInterpretedSchema.transform
validate()
validate(value): Result<Output>;Defined in: schemas/interpretedSchema.ts:89
Checks a value against this schema and returns a Result synchronously. It never throws for an invalid value — a rejection comes back as issues, so the two branches are handled the same way every time.
Parameters
value
unknown
Returns
Result<Output>
Example
import { pvl } from '@pcvl/schema';
const result = pvl.number().int().validate(1.5);
if (result.issues) {
console.log(result.issues[0]?.message); // 'Number must be an integer'
} else {
console.log(result.value);
}