@pcvl/schema / NumberInterpretedSchema
Class: NumberInterpretedSchema<Input, Output>
Defined in: schemas/numberInterpretedSchema.ts:53
Accepts a JavaScript number — floats and integers alike, since JavaScript has only one numeric type. Build one with pvl.number().
NaN is rejected even though typeof NaN === 'number': an accepted NaN would silently fail every .min()/.max() comparison instead of producing a clear Issue. Infinity and -Infinity are accepted — there is no built-in finiteness constraint. Arbitrary-precision integers belong to pvl.bigint(), and this schema never accepts or produces one.
Constraints are checked in the order they were chained, and every one that fails is reported. They chain in any order with the shared modifiers — a constraint keeps whatever modifiers were already applied.
Example
import { pvl } from '@pcvl/schema';
const age = pvl.number().int().min(0).max(130);
age.validate(36); // { value: 36 }
age.validate(36.5); // { issues: [{ code: 'NOT_INTEGER', ... }] }
age.validate(Number.NaN); // { issues: [{ code: 'INVALID_TYPE', ... }] }Extends
ChainableInterpretedSchema<Input,Output>
Type Parameters
Input
Input = number
Output
Output = number
Methods
coerce()
coerce(): NumberInterpretedSchema<unknown, Output>;Defined in: schemas/numberInterpretedSchema.ts:176
Converts a string or a boolean with Number() before the type check, so '42' can satisfy this schema. Any other value is left as it is, and a string that isn't a number becomes NaN, which the type check rejects. Constraints always run after the type check, so chaining it after them is fine.
Returns
NumberInterpretedSchema<unknown, Output>
Example
import { pvl } from '@pcvl/schema';
const port = pvl.number().int().min(1).coerce();
port.validate('8080'); // { value: 8080 }
port.validate('nope'); // { issues: [{ code: 'INVALID_TYPE', ... }] }int()
int(options?): this;Defined in: schemas/numberInterpretedSchema.ts:141
Requires a whole number, as judged by Number.isInteger. Infinity fails it.
Parameters
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const pageSize = pvl.number().int();
pageSize.validate(25); // { value: 25 }
pageSize.validate(25.5); // { issues: [{ code: 'NOT_INTEGER', ... }] }max()
max(maxValue, options?): this;Defined in: schemas/numberInterpretedSchema.ts:109
Requires a value less than or equal to value — the bound is inclusive.
Parameters
maxValue
number
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const percentage = pvl.number().min(0).max(100);
percentage.validate(101); // { issues: [{ code: 'TOO_BIG', ... }] }min()
min(minValue, options?): this;Defined in: schemas/numberInterpretedSchema.ts:79
Requires a value greater than or equal to value — the bound is inclusive.
Parameters
minValue
number
options?
Partial<Pick<IssueProps, "message">>
Returns
this
Example
import { pvl } from '@pcvl/schema';
const quantity = pvl.number().min(1, { message: 'order at least one' });
quantity.validate(1); // { value: 1 }
quantity.validate(0); // { issues: [{ code: 'TOO_SMALL', message: 'order at least one' }] }nullable()
nullable(): NumberInterpretedSchema<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
NumberInterpretedSchema<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(): NumberInterpretedSchema<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
NumberInterpretedSchema<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?): NumberInterpretedSchema<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
NumberInterpretedSchema<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(): NumberInterpretedSchema<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
NumberInterpretedSchema<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);
}