simple, easy-to-use, and declarative schema validator
===
All of web applications need handling input parameters, consists of following steps:
- existence check
- all required parameters exist?
- fill omittable parameters with default values
- type check
- e.g.,
typeof age === "number"
- cast them if needed;
"20"
(string) to20
(number)
- e.g.,
- domain check
- e.g.,
1 <= limit && limit <= 100
- revise them if needed;
0
to1
- e.g.,
value-schema
does all of them, by compact and highly readable code!
import vs from "value-schema";
const schemaObject = { // schema for input
id: vs.number({ // number, >=1
minValue: 1,
}),
name: vs.string({ // string, max 16 characters (trims if over)
maxLength: {
length: 16,
trims: true,
},
}),
age: vs.number({ // number, integer (rounds down toward zero), >=0
integer: vs.NUMBER.INTEGER.FLOOR_RZ,
minValue: 0,
}),
email: vs.email(), // email
state: vs.string({ // string, accepts only "active" and "inactive"
only: ["active", "inactive"],
}),
classes: vs.array({ // array of number, separated by ",", ignores errors
separatedBy: ",",
each: {
schema: vs.number(),
ignoresErrors: true,
},
}),
skills: vs.array({ // array of string, separated by ",", ignores errors
separatedBy: ",",
each: {
schema: vs.string(),
ignoresErrors: true,
},
}),
creditCard: vs.numericString({ // numeric string, separated by "-", checks by Luhn algorithm
separatedBy: "-",
checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.CREDIT_CARD,
}),
remoteAddr: vs.string({ // IPv4
pattern: vs.STRING.PATTERN.IPV4,
}),
remoteAddrIpv6: vs.string({ // IPv6
pattern: vs.STRING.PATTERN.IPV6,
}),
limit: vs.number({ // number, integer, omittable (sets 10 if omitted), >=1 (sets 1 if less), <=100 (sets 100 if greater)
ifUndefined: 10,
integer: true,
minValue: {
value: 1,
adjusts: true,
},
maxValue: {
value: 100,
adjusts: true,
},
}),
offset: vs.number({ // number, integer, omittable (sets 0 if omitted), >=0 (sets 0 if less)
ifUndefined: 0,
integer: true,
minValue: {
value: 0,
adjusts: true,
},
}),
};
const input = { // input values
id: "1",
name: "Pablo Diego JosĂ© Francisco de Paula Juan Nepomuceno MarĂa de los Remedios Ciprin Cipriano de la SantĂsima Trinidad Ruiz y Picasso",
age: 20.5,
email: "picasso@example.com",
state: "active",
classes: "1,3,abc,4",
skills: "c,c++,javascript,python,,swift,kotlin",
creditCard: "4111-1111-1111-1111",
remoteAddr: "127.0.0.1",
remoteAddrIpv6: "::1",
limit: "0",
};
const expected = { // should be converted to this
id: 1,
name: "Pablo Diego José",
age: 20,
email: "picasso@example.com",
state: "active",
classes: [1, 3, 4],
skills: ["c", "c++", "javascript", "python", "swift", "kotlin"],
creditCard: "4111111111111111",
remoteAddr: "127.0.0.1",
remoteAddrIpv6: "::1",
limit: 1,
offset: 0,
};
// Let's apply!
const actual = vs.applySchemaObject(schemaObject, input);
// verification
assert.deepStrictEqual(actual, expected);
That's all! No control flows! Isn't it cool?
For details, see basic usage.
install from npm registry.
npm install -S value-schema
// foo.js
var vs = require("value-schema");
ES Modules / Babel / TypeScript
// foo.mjs (ES Modules) / foo.js (Babel) / foo.ts (TypeScript)
import vs from "value-schema";
ES Modules has been supported as of Node.js v8.5.0.
In Windows, Node.js v8.6.0 is recommended due to ERR_INVALID_PROTOCOL
.
To execute "foo.mjs", --experimental-modules
flag is required.
(the flag is dropped as of Node.js v13.2.0)
$ node --experimental-modules foo.mjs
(node:25508) ExperimentalWarning: The ESM module loader is experimental.
TypeScript auto-completion and type-checking works perfectly on Visual Studio Code and IntelliJ IDEA!
Deno has been supported as of v3.0.0-rc.4.
import vs from "https://deno.land/x/value_schema/mod.ts";
CAUTION: specify value_schema
(underscore) NOT value-schema
(hyphen) because deno.land module database does not support name with hyphen!
The ValueSchemaError
object represents an error.
export interface ValueSchemaError extends Error
{
name: string
message: string
cause: string
value: any
keyStack: (string | number)[]
/**
* check whether error is instance of ValueSchemaError or not
* @param err error to check
* @returns Yes/No
*/
static is(err: unknown): err is ValueSchemaError;
}
name | description |
---|---|
name |
"ValueSchemaError" |
message |
human-readable description of the error, including a string cause |
cause |
cause of error; see CAUSE |
value |
value to apply |
keyStack |
array consists of path to key name(for object) or index(for array) that caused error; for nested object or array |
See below example.
For detail about schema / value-schema
, see basic usage
import vs from "value-schema";
import assert from "assert";
// {foo: Array<{bar: {baz: number}}>}
const schemaObject = {
foo: vs.array({
each: vs.object({
schemaObject: {
bar: vs.object({
schemaObject: {
baz: vs.number(),
},
}),
},
}),
}),
};
const input = {
foo: [
{
bar: {
baz: 1,
},
},
{
bar: {
baz: 2,
},
},
{ // index 2
bar: {
baz: "three", // ERROR!
},
},
{
bar: {
baz: 4,
},
},
],
};
assert.throws(
() => {
vs.applySchemaObject(schemaObject, input);
},
{
name: "ValueSchemaError",
cause: vs.CAUSE.TYPE,
keyStack: ["foo", 2, "bar", "baz"], // route to error key/index: object(key="foo") -> array(index=2) -> object(key="bar") -> object(key="baz")
});
The cause of error.
For more information, see below examples.
Rounding mode.
For more information, see number.
Checksum algorithms for numeric string.
For more information, see numeric string.
Regular expressions for string.
For more information, see string.
apply schemaObject
to data
.
An object to be applied schema; e.g., req.query
, req.body
(in Express)
data
will NOT be overwritten.
Schema object.
- key: property name of
data
- value: schema instance; see below examples
Callback function for each errors. If no errors, this function will not be called.
If this parameter is omitted, applySchemaObject()
throws ValueSchemaError
on first error and remaining adjusting process will be cancelled.
err
- an instance of
ValueSchemaError
err.keyStack
indicates path to key name that caused error:(string | number)[]
- an instance of
- returns
- an adjuted value
- throws
- an exception that will thrown from
applySchemaObject()
- remaining processes will be cancelled
- an exception that will thrown from
Called after finished all error handlings. Will NOT called if no errors.
For more information, see below references about number()
, string()
, and so on.
const schemaObject = { // schema for input
id: vs.number({ // number, >=1
minValue: 1,
}),
name: vs.string({ // string, max 16 characters (trims if over)
maxLength: {
length: 16,
trims: true,
},
}),
age: vs.number({ // number, integer (rounds down toward zero), >=0
integer: vs.NUMBER.INTEGER.FLOOR_RZ,
minValue: 0,
}),
email: vs.email(), // email
state: vs.string({ // string, accepts only "active" and "inactive"
only: ["active", "inactive"],
}),
classes: vs.array({ // array of number, separated by ",", ignores errors
separatedBy: ",",
each: {
schema: vs.number(),
ignoresErrors: true,
},
}),
skills: vs.array({ // array of string, separated by ",", ignores errors
separatedBy: ",",
each: {
schema: vs.string(),
ignoresErrors: true,
},
}),
creditCard: vs.numericString({ // numeric string, separated by "-", checks by Luhn algorithm
separatedBy: "-",
checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.CREDIT_CARD,
}),
remoteAddr: vs.string({ // IPv4
pattern: vs.STRING.PATTERN.IPV4,
}),
remoteAddrIpv6: vs.string({ // IPv6
pattern: vs.STRING.PATTERN.IPV6,
}),
limit: vs.number({ // number, integer, omittable (sets 10 if omitted), >=1 (sets 1 if less), <=100 (sets 100 if greater)
ifUndefined: 10,
integer: true,
minValue: {
value: 1,
adjusts: true,
},
maxValue: {
value: 100,
adjusts: true,
},
}),
offset: vs.number({ // number, integer, omittable (sets 0 if omitted), >=0 (sets 0 if less)
ifUndefined: 0,
integer: true,
minValue: {
value: 0,
adjusts: true,
},
}),
};
const input = { // input values
id: "1",
name: "Pablo Diego JosĂ© Francisco de Paula Juan Nepomuceno MarĂa de los Remedios Ciprin Cipriano de la SantĂsima Trinidad Ruiz y Picasso",
age: 20.5,
email: "picasso@example.com",
state: "active",
classes: "1,3,abc,4",
skills: "c,c++,javascript,python,,swift,kotlin",
creditCard: "4111-1111-1111-1111",
remoteAddr: "127.0.0.1",
remoteAddrIpv6: "::1",
limit: "0",
};
const expected = { // should be converted to this
id: 1,
name: "Pablo Diego José",
age: 20,
email: "picasso@example.com",
state: "active",
classes: [1, 3, 4],
skills: ["c", "c++", "javascript", "python", "swift", "kotlin"],
creditCard: "4111111111111111",
remoteAddr: "127.0.0.1",
remoteAddrIpv6: "::1",
limit: 1,
offset: 0,
};
// Let's apply!
const actual = vs.applySchemaObject(schemaObject, input);
// verification
assert.deepStrictEqual(actual, expected);
In TypeScript, use "Generics" for type-safe.
interface Parameters {
foo: number
bar: string
}
const schemaObject: vs.SchemaObject = {
foo: vs.number(),
bar: vs.string(),
};
const input = {
foo: "12345",
bar: "abcde",
};
const actual = vs.applySchemaObject<Parameters>(schemaObject, input);
fix errors
import vs from "value-schema";
import assert from "assert";
const schemaObject = {
id: vs.number({
minValue: 1,
}),
name: vs.string({
maxLength: {
length: 16,
trims: true,
},
}),
email: vs.email(),
};
const input = {
id: 0, // error! (>= 1)
name: "", // error! (empty string is not allowed)
email: "john@example.com", // OK
};
const expected = {
id: 100,
name: "John Doe",
email: "john@example.com",
};
const actual = vs.applySchemaObject(schemaObject, input, (err) => {
const key = err.keyStack.shift();
switch(key) {
case "id":
return 100;
case "name":
return "John Doe";
default:
return null;
}
});
assert.deepStrictEqual(actual, expected);
throw exception after finished
import vs from "value-schema";
import assert from "assert";
const schemaObject = {
id: vs.number({
minValue: 1,
}),
name: vs.string({
maxLength: {
length: 16,
trims: true,
},
}),
email: vs.email(),
};
const input = {
id: 0, // error! (>= 1)
name: "", // error! (empty string is not allowed)
email: "john@example.com", // OK
};
assert.throws(() => {
const messages = [];
vs.applySchemaObject(schemaObject, input, (err) => {
// append key name
const key = err.keyStack.shift();
if(key !== undefined) {
messages.push(key);
}
}, () => {
// finished; join key name as message
throw Error(messages.sort().join(","));
});
}, {
name: "Error",
message: "id,name",
});
catch a first error by omitting error handler
import vs from "value-schema";
import assert from "assert";
const schemaObject = {
id: vs.number({
minValue: 1,
}),
name: vs.string({
maxLength: {
length: 16,
trims: true,
},
}),
email: vs.email(),
};
const input = {
id: 0, // error! (>= 1)
name: "", // error! (empty string is not allowed)
email: "john@example.com", // OK
};
assert.throws(() => {
// throws first error
vs.applySchemaObject(schemaObject, input);
}, {
name: "ValueSchemaError",
cause: vs.CAUSE.MIN_VALUE,
value: 0,
keyStack: ["id"],
});
when input value is not an object
NOTE: schemaObject
won't be checked because it's predictable; generated by programmer, not an external input
import vs from "value-schema";
import assert from "assert";
const schemaObject = {};
const input = 123;
assert.throws(() => {
// `input` must be an object
vs.applySchemaObject(schemaObject, input);
}, {
name: "ValueSchemaError",
cause: vs.CAUSE.TYPE,
value: 123,
keyStack: [],
});
export function boolean(options?: OptionsForBoolean): BooleanSchema;
type OptionsForBoolean = {
strictType?: boolean;
acceptsAllNumbers?: boolean;
ifUndefined?: boolean | null;
ifEmptyString?: boolean | null;
ifNull?: boolean | null;
}
type ErrorHandler = (err: ValueSchemaError) => boolean | null | never;
interface BooleanSchema {
applyTo(value: unknown, onError?: ErrorHandler): boolean | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.strictEqual(
vs.boolean().applyTo(true),
true);
assert.strictEqual(
vs.boolean().applyTo(false),
false);
// should be adjusted
assert.strictEqual(
vs.boolean().applyTo(1),
true);
assert.strictEqual(
vs.boolean().applyTo(0),
false);
assert.strictEqual(
vs.boolean().applyTo("1"),
true);
assert.strictEqual(
vs.boolean().applyTo("0"), // "0" is truthy in JavaScript, but value-schema treats as false!
false);
// other truthy values
for (const truthy of ["true", "TRUE", "yes", "YES", "on", "ON"]) {
assert.strictEqual(
vs.boolean().applyTo(truthy),
true);
}
// other falsy values
for (const falsy of ["false", "FALSE", "no", "NO", "off", "OFF"]) {
assert.strictEqual(
vs.boolean().applyTo(falsy),
false);
}
// should cause error
assert.throws(
() => vs.boolean().applyTo(-1), // accepts only 0,1
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.boolean().applyTo("abc"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.boolean().applyTo([]),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.boolean().applyTo({}),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Enable strict type check. defaults: false
HANDLE WITH CARE!
In URL encoding, all values will be treated as string.
Use this method when your system accepts ONLY JSON encoding (application/json
)
// should cause error
assert.throws(
() => vs.boolean({strictType: true}).applyTo(1),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.boolean({strictType: true}).applyTo("1"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.boolean({strictType: true}).applyTo("true"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Accepts all numbers or not, other than 0 / 1. defaults: false
// should be adjusted
assert.strictEqual(
vs.boolean({acceptsAllNumbers: true}).applyTo(-1),
true);
assert.strictEqual(
vs.boolean({acceptsAllNumbers: true}).applyTo("100"),
true);
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.boolean({ifUndefined: true}).applyTo(undefined),
true);
// should cause error
assert.throws(
() => vs.boolean().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.strictEqual(
vs.boolean({ifNull: true}).applyTo(null),
true);
// should cause error
assert.throws(
() => vs.boolean().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.strictEqual(
vs.boolean({ifEmptyString: true}).applyTo(""),
true);
// should cause error
assert.throws(
() => vs.boolean().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
export function number(options?: OptionsForNumber): NumberSchema;
type OptionsForNumber = {
strictType?: boolean;
acceptsSpecialFormats?: boolean;
acceptsFullWidth?: boolean;
ifUndefined?: number | null;
ifEmptyString?: number | null;
ifNull?: number | null;
integer?: boolean | NUMBER.INTEGER;
only?: number[];
minValue?: number | {value: number, adjusts: boolean};
maxValue?: number | {value: number, adjusts: boolean};
converter?: (value: number, fail: () => never) => number | null;
}
type ErrorHandler = (err: ValueSchemaError) => number | null | never;
interface NumberSchema {
applyTo(value: unknown, onError?: ErrorHandler): number | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.strictEqual(
vs.number().applyTo(-123),
-123);
// should be adjusted
assert.strictEqual(
vs.number().applyTo("-123"),
-123);
assert.strictEqual(
vs.number().applyTo(true),
1);
assert.strictEqual(
vs.number().applyTo(false),
0);
// should cause error
assert.strictEqual( // catch error by callback function (that returns a value from applyTo() method)
vs.number().applyTo(
"abc",
(err) => 10),
10);
assert.throws( // ... or try-catch syntax
() => vs.number().applyTo("abc"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.number().applyTo("true"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Enable strict type check. defaults: false
HANDLE WITH CARE!
In URL encoding, all values will be treated as string.
Use this method when your system accepts ONLY JSON encoding (application/json
)
// should be adjusted
assert.strictEqual(
vs.number().applyTo("123"),
123);
assert.strictEqual(
vs.number().applyTo(true),
1);
// should cause error
assert.throws(
() => vs.number({strictType: true}).applyTo("123"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.number({strictType: true}).applyTo(true),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.number({ifUndefined: 1}).applyTo(undefined),
1);
// should cause error
assert.throws(
() => vs.number().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.strictEqual(
vs.number({ifNull: 1}).applyTo(null),
1);
// should cause error
assert.throws(
() => vs.number().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.strictEqual(
vs.number({ifEmptyString: 1}).applyTo(""),
1);
// should cause error
assert.throws(
() => vs.number().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Accepts all special number formats; e.g., "1e+2"
, "0x100"
, "0o100"
, "0b100"
.
defaults: false
// should be adjusted
assert.strictEqual(
vs.number({acceptsSpecialFormats: true}).applyTo("1e+2"),
100);
assert.strictEqual(
vs.number({acceptsSpecialFormats: true}).applyTo("0x100"),
256);
assert.strictEqual(
vs.number({acceptsSpecialFormats: true}).applyTo("0o100"),
64);
assert.strictEqual(
vs.number({acceptsSpecialFormats: true}).applyTo("0b100"),
4);
// should cause error
assert.throws(
() => vs.number().applyTo("1e+2"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Accepts full-width string; e.g., "1234.5"
, "1234.5"
.
defaults: false
// should be adjusted
assert.strictEqual(
vs.number({acceptsFullWidth: true}).applyTo("1234.5"),
1234.5);
// should cause error
assert.throws(
() => vs.number().applyTo("1234.5"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Limits an input value to integer.
value | description |
---|---|
NUMBER.INTEGER.NO (0 ) / false |
does not limit to integer |
NUMBER.INTEGER.YES (1 ) / true |
limits to integer, but does not round |
NUMBER.INTEGER.FLOOR (2 ) |
rounds down toward infinity |
NUMBER.INTEGER.FLOOR_RZ (3 ) |
rounds down toward zero |
NUMBER.INTEGER.CEIL (4 ) |
rounds up toward infinity |
NUMBER.INTEGER.CEIL_RZ (5 ) |
rounds up toward zero |
NUMBER.INTEGER.HALF_UP (6 ) |
rounds half up toward infinity |
NUMBER.INTEGER.HALF_UP_RZ (7 ) |
rounds half up toward zero |
NUMBER.INTEGER.HALF_DOWN (8 ) |
rounds half down toward infinity |
NUMBER.INTEGER.HALF_DOWN_RZ (9 ) |
rounds half down toward zero |
// should be adjusted
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.FLOOR}).applyTo(3.14),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.FLOOR}).applyTo("3.14"),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.FLOOR}).applyTo(-3.14),
-4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.FLOOR_RZ}).applyTo(3.14),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.FLOOR_RZ}).applyTo(-3.14),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.CEIL}).applyTo(3.14),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.CEIL}).applyTo(-3.14),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.CEIL_RZ}).applyTo(3.14),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.CEIL_RZ}).applyTo(-3.14),
-4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP}).applyTo(3.49),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP}).applyTo(3.5),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP}).applyTo(-3.5),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP}).applyTo(-3.51),
-4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP_RZ}).applyTo(3.49),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP_RZ}).applyTo(3.5),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP_RZ}).applyTo(-3.49),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_UP_RZ}).applyTo(-3.5),
-4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN}).applyTo(3.5),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN}).applyTo(3.51),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN}).applyTo(-3.49),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN}).applyTo(-3.5),
-4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN_RZ}).applyTo(3.5),
3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN_RZ}).applyTo(3.51),
4);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN_RZ}).applyTo(-3.5),
-3);
assert.strictEqual(
vs.number({integer: vs.NUMBER.INTEGER.HALF_DOWN_RZ}).applyTo(-3.51),
-4);
// should cause error
assert.throws(
() => vs.number({integer: true}).applyTo(3.14),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.number({integer: vs.NUMBER.INTEGER.YES}).applyTo(3.14), // equivalent to "true"
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Accepts only particular values.
// should be OK
assert.strictEqual(
vs.number({only: [1, 3, 5]}).applyTo(1),
1);
// should cause error
assert.throws(
() => vs.number({only: [1, 3, 5]}).applyTo(2),
{name: "ValueSchemaError", cause: vs.CAUSE.ONLY});
Limits minimum value.
// should be adjusted
assert.strictEqual(
vs.number({minValue: {value: 1, adjusts: true}}).applyTo(0),
1);
// should cause errors
assert.throws(
() => vs.number({minValue: {value: 1, adjusts: false}}).applyTo(0),
{name: "ValueSchemaError", cause: vs.CAUSE.MIN_VALUE});
assert.throws(
() => vs.number({minValue: 1}).applyTo(0), // shorthand of {value: 1, adjusts: false}
{name: "ValueSchemaError", cause: vs.CAUSE.MIN_VALUE});
Limits maximum value.
// should be adjusted
assert.strictEqual(
vs.number({maxValue: {value: 100, adjusts: true}}).applyTo(101),
100);
// should cause errors
assert.throws(
() => vs.number({maxValue: {value: 100, adjusts: false}}).applyTo(101),
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_VALUE});
assert.throws(
() => vs.number({maxValue: 100}).applyTo(101), // shorthand of {value: 100, adjusts: false}
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_VALUE});
Converts input value to another.
fail()
causes ValueSchemaError
.
// should be adjusted
assert.strictEqual(
vs.number({converter: value => value * 2}).applyTo("1"),
2);
// should cause errors
assert.throws(
() => vs.number({converter: (value, fail) => fail()}).applyTo(0),
{name: "ValueSchemaError", cause: vs.CAUSE.CONVERTER});
export function string(options?: OptionsForString): StringSchema;
type OptionsForString = {
strictType?: boolean;
ifUndefined?: string | null;
ifEmptyString?: string | null;
ifNull?: string | null;
trims?: boolean;
only?: string[];
minLength?: number;
maxLength?: number | {length: number, trims: boolean};
pattern?: RegExp;
converter?: (value: string, fail: () => never) => string | null;
}
type ErrorHandler = (err: ValueSchemaError) => string | null | never;
interface StringSchema {
applyTo(value: unknown, onError?: ErrorHandler): string | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.strictEqual(
vs.string().applyTo("123"),
"123");
// should be adjusted
assert.strictEqual(
vs.string().applyTo(123),
"123");
// should cause error
assert.throws(
() => vs.string().applyTo({}),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Enable strict type check. defaults: false
// should be adjusted
assert.strictEqual(
vs.string().applyTo(123),
"123");
assert.strictEqual(
vs.string().applyTo(true),
"true");
// should cause error
assert.throws(
() => vs.string({strictType: true}).applyTo(123),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.string({strictType: true}).applyTo(true),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.string({ifUndefined: "xyz"}).applyTo(undefined),
"xyz");
// should cause error
assert.throws(
() => vs.string().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.strictEqual(
vs.string({ifNull: "x"}).applyTo(null),
"x");
// should cause error
assert.throws(
() => vs.string().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.string({ifEmptyString: "xyz"}).applyTo(""),
"xyz");
// should cause error
assert.throws(
() => vs.string().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Removes whitespace from both ends of input. defaults: false
// should be adjusted
assert.strictEqual(
vs.string({trims: true}).applyTo("\r\n hell, word \t "),
"hell, word");
// should cause error
assert.throws(
() => vs.string({trims: true}).applyTo(" \t\r\n "),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Accepts only particular values.
// should be OK
assert.strictEqual(
vs.string({only: ["eat", "sleep", "play"]}).applyTo("sleep"),
"sleep");
assert.strictEqual(
vs.string({only: [""]}).applyTo(""),
"");
// should cause error
assert.throws(
() => vs.string({only: ["eat", "sleep", "play"]}).applyTo("study"),
{name: "ValueSchemaError", cause: vs.CAUSE.ONLY});
Limits minimum length of input string.
// should be OK
assert.strictEqual(
vs.string({minLength: 5}).applyTo("abcde"),
"abcde");
// should cause error
assert.throws(
() => vs.string({minLength: 5}).applyTo("a"),
{name: "ValueSchemaError", cause: vs.CAUSE.MIN_LENGTH});
Limits maximum length of an input string.
// should be OK
assert.strictEqual(
vs.string({maxLength: {length: 5, trims: false}}).applyTo("abcde"),
"abcde");
// should be adjusted
assert.strictEqual(
vs.string({maxLength: {length: 5, trims: true}}).applyTo("abcdefg"),
"abcde");
// should cause error
assert.throws(
() => vs.string({maxLength: {length: 5, trims: false}}).applyTo("abcdefg"),
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
assert.throws(
() => vs.string({maxLength: 5}).applyTo("abcdefg"), // shorthand of {length: 5, trims: false}
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
Specifies acceptable pattern by regular expression.
You can also use STRING.PATTERN
constants
constant | explanation |
---|---|
STRING.PATTERN.EMAIL |
email address that follows RFC5321 / RFC5322 |
STRING.PATTERN.HTTP |
HTTP/HTTPS URL |
STRING.PATTERN.IPV4 |
IPv4 address |
STRING.PATTERN.IPV6 |
IPv6 address |
STRING.PATTERN.URI |
URI that follows RFC3986 |
// should be OK
assert.deepStrictEqual(
vs.string({pattern: /^Node.js$/}).applyTo("NodeXjs"),
"NodeXjs");
assert.deepStrictEqual(
vs.string({pattern: vs.STRING.PATTERN.URI}).applyTo("https://example.com/path/to/resource?name=value#hash"),
"https://example.com/path/to/resource?name=value#hash");
// should cause error
assert.throws(
() => vs.string({pattern: /^Node.js$/}).applyTo("NODE.JS"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.string({pattern: vs.STRING.PATTERN.URI}).applyTo("https://äľ‹.com/"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
Converts input value to another.
fail()
causes ValueSchemaError
.
// should be adjusted
assert.strictEqual(
vs.string({converter: value => value.toLowerCase()}).applyTo("123ABCxyz"),
"123abcxyz");
// should cause errors
assert.throws(
() => vs.string({converter: (value, fail) => fail()}).applyTo("foo"),
{name: "ValueSchemaError", cause: vs.CAUSE.CONVERTER});
export function numericString(options?: OptionsForNumericString): NumericStringSchema;
type OptionsForNumericString = {
ifUndefined?: string | null;
ifEmptyString?: string | null;
ifNull?: string | null;
fullWidthToHalf?: boolean;
joinsArray?: boolean;
minLength?: number;
maxLength?: number | {length: number, trims: boolean};
separatedBy?: string | RegExp;
pattern?: RegExp;
checksum?: NUMERIC_STRING.CHECKSUM_ALGORITHM;
converter?: (value: string, fail: () => never) => string | null;
}
type ErrorHandler = (err: ValueSchemaError) => string | null | never;
interface NumericStringSchema {
applyTo(value: unknown, onError?: ErrorHandler): string | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.strictEqual(
vs.numericString().applyTo("123"),
"123");
// should be adjusted
assert.strictEqual(
vs.numericString().applyTo(123),
"123");
// should cause error
assert.throws(
() => vs.numericString().applyTo("abc"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.numericString({ifUndefined: "123"}).applyTo(undefined),
"123");
// should cause error
assert.throws(
() => vs.numericString().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.strictEqual(
vs.numericString({ifNull: "456"}).applyTo(null),
"456");
// should cause error
assert.throws(
() => vs.numericString().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.strictEqual(
vs.numericString({ifEmptyString: "456"}).applyTo(""),
"456");
// should cause error
assert.throws(
() => vs.numericString().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Assumes an input value is separated by delimiter, and ignore them.
// should be adjusted
assert.strictEqual(
vs.numericString({separatedBy: "-"}).applyTo("4111-1111-1111-1111"),
"4111111111111111");
// should cause error
assert.throws(
() => vs.numericString().applyTo("4111-1111-1111-1111"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
Converts full-width string to half-width; e.g., "1234"
.
defaults: false
// should be adjusted
assert.strictEqual(
vs.numericString({fullWidthToHalf: true}).applyTo("1234"),
"1234");
// should cause error
assert.throws(
() => vs.numericString().applyTo("1234"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
Assumes an input value is array, and join them. defaults: false
This method is useful for the following form.
<!-- "cc_number" will be passed in array -->
<form>
Input credit card number:
<input name="cc_number" required />
-
<input name="cc_number" required />
-
<input name="cc_number" required />
-
<input name="cc_number" required />
</form>
// should be adjusted
assert.strictEqual(
vs.numericString({joinsArray: true}).applyTo(["1234", "5678"]),
"12345678");
// should cause error
assert.throws(
() => vs.numericString().applyTo(["1234", "5678"]),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Limits minimum length of input string.
// should be OK
assert.strictEqual(
vs.numericString({minLength: 4}).applyTo("1234"),
"1234");
// should cause error
assert.throws(
() => vs.numericString({minLength: 5}).applyTo("1234"),
{name: "ValueSchemaError", cause: vs.CAUSE.MIN_LENGTH});
Limits maximum length of an input string.
// should be OK
assert.strictEqual(
vs.numericString({maxLength: {length: 4, trims: false}}).applyTo("1234"),
"1234");
// should be adjusted
assert.strictEqual(
vs.numericString({maxLength: {length: 5, trims: true}, separatedBy: "-"}).applyTo("1234-5678"),
"12345");
// should cause error
assert.throws(
() => vs.numericString({maxLength: {length: 5, trims: false}}).applyTo("123456"),
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
assert.throws(
() => vs.numericString({maxLength: 5}).applyTo("123456"), // shorthand of {length: 5, trims: false}
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
Checks input value by specified algorithm.
algorithm name | explanation | used by | constant | aliases |
---|---|---|---|---|
"luhn" |
Luhn algorithm | credit card | NUMERIC_STRING.CHECKSUM_ALGORITHM.LUHN |
NUMERIC_STRING.CHECKSUM_ALGORITHM.CREDIT_CARD |
"modulus10/weight3:1" |
Modulus 10 / Weight 3:1 | ISBN-13, EAN, JAN | NUMERIC_STRING.CHECKSUM_ALGORITHM.MODULUS10_WEIGHT3_1 |
NUMERIC_STRING.CHECKSUM_ALGORITHM.ISBN13 / NUMERIC_STRING.CHECKSUM_ALGORITHM.EAN / NUMERIC_STRING.CHECKSUM_ALGORITHM.JAN |
// should be OK
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.LUHN}).applyTo("4111111111111111"),
"4111111111111111");
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.CREDIT_CARD}).applyTo("4111111111111111"), // alias of LUHN
"4111111111111111");
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.MODULUS10_WEIGHT3_1}).applyTo("9784101092058"),
"9784101092058");
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.ISBN13}).applyTo("9784101092058"), // alias of MODULUS10_WEIGHT3_1
"9784101092058");
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.EAN}).applyTo("9784101092058"), // alias of MODULUS10_WEIGHT3_1
"9784101092058");
assert.strictEqual(
vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.JAN}).applyTo("9784101092058"), // alias of MODULUS10_WEIGHT3_1
"9784101092058");
// should cause error
assert.throws(
() => vs.numericString({checksum: vs.NUMERIC_STRING.CHECKSUM_ALGORITHM.LUHN}).applyTo("4111111111111112"),
{name: "ValueSchemaError", cause: vs.CAUSE.CHECKSUM});
Converts input value to another.
fail()
causes ValueSchemaError
.
// should be adjusted
assert.strictEqual(
vs.numericString({converter: value => value.padStart(8, "0")}).applyTo("1234"),
"00001234");
// should cause errors
assert.throws(
() => vs.numericString({converter: (value, fail) => fail()}).applyTo("1234"),
{name: "ValueSchemaError", cause: vs.CAUSE.CONVERTER});
export function email(options?: OptionsForEmail): EmailSchema;
type OptionsForEmail = {
ifUndefined?: string | null;
ifEmptyString?: string | null;
ifNull?: string | null;
trims?: boolean;
pattern?: RegExp;
}
type ErrorHandler = (err: ValueSchemaError) => string | null | never;
interface EmailSchema {
applyTo(value: unknown, onError?: ErrorHandler): string | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.strictEqual(
vs.email().applyTo("user+mailbox/department=shipping@example.com"),
"user+mailbox/department=shipping@example.com"); // dot-string
assert.strictEqual(
vs.email().applyTo("!#$%&'*+-/=?^_`.{|}~@example.com"),
"!#$%&'*+-/=?^_`.{|}~@example.com"); // dot-string
assert.strictEqual(
vs.email().applyTo("\"Fred\\\"Bloggs\"@example.com"),
"\"Fred\\\"Bloggs\"@example.com"); // quoted-string
assert.strictEqual(
vs.email().applyTo("\"Joe.\\\\Blow\"@example.com"),
"\"Joe.\\\\Blow\"@example.com"); // quoted-string
assert.strictEqual(
vs.email().applyTo("user@example-domain.com"),
"user@example-domain.com");
assert.strictEqual(
vs.email().applyTo("user@example2.com"),
"user@example2.com");
// should cause error
assert.throws(
() => vs.email().applyTo("@example.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo(".a@example.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("a.@example.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("a..a@example.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("user@example@com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("user-example-com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("user@example_domain.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email().applyTo("user@example.com2"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
Specifies return value when input value is undefined
.
// should be adjusted
assert.strictEqual(
vs.email({ifUndefined: "user@example.com"}).applyTo(undefined),
"user@example.com");
// should cause error
assert.throws(
() => vs.email().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.strictEqual(
vs.email({ifNull: "user@example.com"}).applyTo(null),
"user@example.com");
// should cause error
assert.throws(
() => vs.email().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.strictEqual(
vs.email({ifEmptyString: "user@example.com"}).applyTo(""),
"user@example.com");
// should cause error
assert.throws(
() => vs.email().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Removes whitespace from both ends of input. defaults: false
// should be adjusted
assert.strictEqual(
vs.email({trims: true}).applyTo("\r\n user@example.com \t "),
"user@example.com");
// should cause error
assert.throws(
() => vs.email().applyTo("\r\n user@example.com1 \t "),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
assert.throws(
() => vs.email({trims: true}).applyTo(" \t\r\n "),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Specifies acceptable pattern by regular expression.
// should be OK
assert.strictEqual(
vs.email({pattern: /^[\w\.]+@([\w\-]+\.)+\w+$/}).applyTo("......@example.com"), // accept leading/trailing/consecutively dots
"......@example.com");
// should cause errors
assert.throws(
() => vs.email().applyTo("......@example.com"),
{name: "ValueSchemaError", cause: vs.CAUSE.PATTERN});
export function array<T>(options?: OptionsForArray<T>): ArraySchema;
type OptionsForArray<T> = {
ifUndefined?: T[] | null;
ifEmptyString?: T[] | null;
ifNull?: T[] | null;
separatedBy?: string | RegExp;
toArray?: boolean;
minLength?: number;
maxLength?: number | {length: number, trims: boolean};
each?: BaseSchema<T> | {schema: BaseSchema<T>, ignoresErrors: boolean};
converter?: (values: T[], fail: () => never) => T[] | null;
}
type ErrorHandler<T> = (err: ValueSchemaError) => T[] | null | never;
interface ArraySchema<T> {
applyTo(value: unknown, onError?: ErrorHandler): T[] | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.deepStrictEqual(
vs.array().applyTo([1, "a"]),
[1, "a"]);
// should cause error
assert.throws(
() => vs.array().applyTo("abc"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.array().applyTo(0),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Specifies return value when input value is undefined
.
// should be adjusted
assert.deepStrictEqual(
vs.array({ifUndefined: [1, "a"]}).applyTo(undefined),
[1, "a"]);
// should cause error
assert.throws(
() => vs.array().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.deepStrictEqual(
vs.array({ifNull: [1, "a"]}).applyTo(null),
[1, "a"]);
// should cause error
assert.throws(
() => vs.array().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.deepStrictEqual(
vs.array({ifEmptyString: [1, "a"]}).applyTo(""),
[1, "a"]);
// should cause error
assert.throws(
() => vs.array().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Assumes an input value is string and separated by delimiter.
If an input type is array, this option will be ignored.
// should be OK
assert.deepStrictEqual(
vs.array({separatedBy: ","}).applyTo([1, 2, 3]),
[1, 2, 3]);
// should be adjusted
assert.deepStrictEqual(
vs.array({separatedBy: ","}).applyTo("1,2,3"),
["1", "2", "3"]);
// should cause error
assert.throws(
() => vs.array().applyTo("1,2,3"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Converts an input value to array if not. defaults: false
// should be OK
assert.deepStrictEqual(
vs.array({toArray: true}).applyTo([0]),
[0]);
// should be adjusted
assert.deepStrictEqual(
vs.array({toArray: true}).applyTo(0),
[0]);
// should cause error
assert.throws(
() => vs.array().applyTo(0),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Limits minimum length of input array.
// should be OK
assert.deepStrictEqual(
vs.array({minLength: 2}).applyTo([1, 2]),
[1, 2]);
// should cause errors
assert.throws(
() => vs.array({minLength: 2}).applyTo([1]),
{name: "ValueSchemaError", cause: vs.CAUSE.MIN_LENGTH});
Limits maximum length of an input array.
// should be OK
assert.deepStrictEqual(
vs.array({maxLength: {length: 2, trims: false}}).applyTo([1, 2]),
[1, 2]);
// should be adjusted
assert.deepStrictEqual(
vs.array({maxLength: {length: 2, trims: true}}).applyTo([1, 2, 3]),
[1, 2]);
// should cause error
assert.throws(
() => vs.array({maxLength: {length: 2, trims: false}}).applyTo([1, 2, 3]),
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
assert.throws(
() => vs.array({maxLength: 2}).applyTo([1, 2, 3]), // shorthand of {length: 1, trims: false}
{name: "ValueSchemaError", cause: vs.CAUSE.MAX_LENGTH});
Apply schema for each elements.
// should be adjusted
assert.deepStrictEqual(
vs.array({each: {schema: vs.number(), ignoresErrors: true}}).applyTo([true, "abc", 2]),
[1, 2]);
// should cause error
assert.throws(
() => vs.array({each: {schema: vs.number(), ignoresErrors: false}}).applyTo([true, "abc", 2]),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.array({each: vs.number()}).applyTo([true, "abc", 2]), // shorthand of {schema: vs.number(), ignoresErrors: false}
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Converts input value to another.
fail()
causes ValueSchemaError
.
// should be adjusted
assert.deepStrictEqual(
vs.array({each: vs.number(), separatedBy: ",", converter: values => values.sort()}).applyTo("4,1,5,2"),
[1, 2, 4, 5]);
// should cause errors
assert.throws(
() => vs.array({converter: (value, fail) => fail()}).applyTo([]),
{name: "ValueSchemaError", cause: vs.CAUSE.CONVERTER});
export function object(options?: OptionsForObject): ObjectSchema;
type OptionsForObject = {
ifUndefined?: object | null;
ifEmptyString?: object | null;
ifNull?: object | null;
schemaObject?: SchemaObject;
converter?: (values: object, fail: () => never) => object | null;
}
type ErrorHandler = (err: ValueSchemaError) => object | null | never;
interface ObjectSchema {
applyTo(value: unknown, onError?: ErrorHandler): object | null
}
Applies schema to value
.
If an error occurs, this method calls onError
(if specified) or throw ValueSchemaError
(otherwise).
// should be OK
assert.deepStrictEqual(
vs.object().applyTo({a: 1, b: 2}),
{a: 1, b: 2});
// should cause error
assert.throws(
() => vs.object().applyTo("abc"),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
assert.throws(
() => vs.object().applyTo(0),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Specifies return value when input value is undefined
.
// should be adjusted
assert.deepStrictEqual(
vs.object({ifUndefined: {a: 1, b: 2}}).applyTo(undefined),
{a: 1, b: 2});
// should cause error
assert.throws(
() => vs.object().applyTo(undefined),
{name: "ValueSchemaError", cause: vs.CAUSE.UNDEFINED});
Specifies return value when input value is null
.
// should be adjusted
assert.deepStrictEqual(
vs.object({ifNull: {a: 1, b: 2}}).applyTo(null),
{a: 1, b: 2});
// should cause error
assert.throws(
() => vs.object().applyTo(null),
{name: "ValueSchemaError", cause: vs.CAUSE.NULL});
Specifies return value when input value is ""
.
// should be adjusted
assert.deepStrictEqual(
vs.object({ifEmptyString: {a: 1, b: 2}}).applyTo(""),
{a: 1, b: 2});
// should cause error
assert.throws(
() => vs.object().applyTo(""),
{name: "ValueSchemaError", cause: vs.CAUSE.EMPTY_STRING});
Applies schemaObject
to input value.
// should be OK
const schemaObject = {a: vs.number(), b: vs.string()};
assert.deepStrictEqual(
vs.object({schemaObject}).applyTo({a: 1, b: "2"}),
{a: 1, b: "2"});
// should be adjusted
assert.deepStrictEqual(
vs.object({schemaObject}).applyTo({a: 1, b: 2}),
{a: 1, b: "2"});
// should cause error
assert.throws(
() => vs.object({schemaObject}).applyTo({a: "x", b: "2"}),
{name: "ValueSchemaError", cause: vs.CAUSE.TYPE});
Converts input value to another.
fail()
causes ValueSchemaError
.
Below example uses case package.
// should be adjusted
function keysToCamel(values) {
return Object.entries(values).reduce((prev, [key, value]) => {
return {
...prev,
[Case.camel(key)]: value,
};
}, {});
}
const input = {
"first name": "John",
"last-name": "Doe",
"credit_card": "4111111111111111",
};
const output = {
firstName: "John",
lastName: "Doe",
creditCard: "4111111111111111",
}
assert.deepStrictEqual(
vs.object({converter: keysToCamel}).applyTo(input),
output);
// should cause errors
assert.throws(
() => vs.object({converter: (value, fail) => fail()}).applyTo({}),
{name: "ValueSchemaError", cause: vs.CAUSE.CONVERTER});
See CHANGELOG.md.