DATA()function
Create a DataSchema for a set of properties.
DATA(props: Schemas<T>): DataSchema<T>
Schema validation for TypeScript. A Schema<T> describes how to coerce and validate an unknown value into a typed T. Schemas are the foundation of shelving — database collections, API endpoints, and form handlers all build on them.
By convention, instantiated schemas are constants and use UPPERCASE.
Schema<T> interfaceEvery schema has a single Schema.validate() method — validate(value: unknown): T. If the value is valid (after any coercion), it returns T. If it is not, it throws a plain string error message — for example "Must be 5–50 characters" or "Required". This is intentional: form handlers and API layers can consume these strings directly without any unwrapping.
Multi-field errors from DataSchema are joined by \n, one line per failed field, with the field name prepended: "name: Required\nemail: Invalid format".
validate() is called with undefined when no value is provided; the schema falls back to its Schema.value default.
Schema functionality in Shelving follows the robustness principle: "be conservative in what you do, be liberal in what you accept from others". Values that are obvious in their intent are coerced to appropriate valid values.
StringSchema converts numbers to strings, BooleanSchema converts the strings "no" and "false" to false and tests other values for truthiness, ArraySchema splits comma-separated strings into arrays, NumberSchema parses strings into numbers.
Every schema carries optional display metadata set at construction time:
| Property | Default | Purpose |
|---|---|---|
Schema.one | "value" | Singular noun, e.g. "product" |
Schema.many | one + "s" | Plural noun, e.g. "products" |
Schema.title | "" | Human-readable label for a field |
Schema.description | "" | Longer description for a field |
Schema.placeholder | "" | Placeholder text for an input |
Schema.value | schema-specific | Default used when validate(undefined) is called |
Schema.format() | schema-specific | Display formatter for downstream form and UI use |
For convenience the shelving/schema module offers sugar constants and factories to improve the readability of code that creates complex schemas. Sugar instances are pre-instantiated copies of Schema classes — by convention an instantiated schema is named in ALL_CAPS, a convention you will find it convenient to use in your own codebase too. Sugar factories are functions like OPTIONAL() and DATA() that build a configured schema for you.
| Sugar instance / factory | Validated type |
|---|---|
STRING | string |
NUMBER | number |
CURRENCY_AMOUNT(code) | number |
BOOLEAN | boolean |
DATA(props) | T (plain object) |
ARRAY(itemSchema) | readonly T[] |
CHOICE(options) | union of string keys |
NULLABLE(schema) | T | null |
OPTIONAL(schema) | T | undefined |
PARTIAL(schema) | Partial<T> |
ITEM(idSchema, props) | { id: I } & T |
Schemas compose: primitives, wrappers (NULLABLE(), OPTIONAL(), ARRAY()), and DATA() nest freely to describe an entire payload in a single validator. See each schema's own page for detailed usage of that class and its constants.
import { ITEM, STRING, REQUIRED_STRING, NUMBER, ARRAY, OPTIONAL, INTEGER } from "shelving/schema";
const PRODUCT = ITEM(INTEGER, {
name: REQUIRED_STRING,
price: NUMBER,
tags: ARRAY(STRING),
notes: OPTIONAL(STRING),
});
PRODUCT.validate({ id: 1, name: "Widget", price: 9.99, tags: "a,b" });
// { id: 1, name: "Widget", price: 9.99, tags: ["a", "b"], notes: undefined }
PRODUCT.validate({ id: 2, name: "", price: 9.99, tags: [] });
// throws "name: Required"Create a DataSchema for a set of properties.
DATA(props: Schemas<T>): DataSchema<T>
Create a schema for a valid data object with specified properties, or null.
NULLABLE_DATA(props: Schemas<T>): NullableSchema<T>
Create a DataSchema that validates partially, i.e. every property can be its value or undefined.
PARTIAL(source: Schemas<T> | DataSchema<T>): DataSchema<PartialData<T>>
Create a DataSchema that validates a data item, i.e. it has a string or number .id identifier property.
ITEM(id: Schema<I>, schemas: Schemas<T> | DataSchema<T>): DataSchema<Item<I, T>>
Create a CurrencyAmountSchema for a non-negative monetary amount in a currency.
CURRENCY_AMOUNT(currency: CurrencyCode): CurrencyAmountSchema
Create a NullableSchema for an optional monetary amount in a currency, or null.
NULLABLE_CURRENCY_AMOUNT(currency: CurrencyCode): NullableSchema<number>
Create a schema for a valid array with specified items.
ARRAY(items: Schema<T>): ArraySchema<T>
Create a schema for a valid dictionary object with specified entry values.
DICTIONARY(items: Schema<T>): DictionarySchema<T>
Create a NullableSchema that wraps a source schema and also allows null.
NULLABLE(source: Schema<T>): NullableSchema<T>
Create an OptionalSchema that wraps a source schema and also allows undefined.
OPTIONAL(source: Schema<T>): OptionalSchema<T>
Create a schema for a valid choice from an allowed set of values.
CHOICE(options: PossibleChoiceOptions<K>): ChoiceSchema<K>
Create a RequiredSchema that wraps a source schema and rejects a falsy result.
REQUIRED(source: Schema<T>): RequiredSchema<T>
Schema that defines a valid string.
new StringSchema({
one = "string",
min = 0,
max = Number.POSITIVE_INFINITY,
value = "",
multiline = false,
rows = multiline ? 2 : 1,
match,
case: _case,
input = "text",
...options
}: StringSchemaOptions)Schema that validates a postal address.
new AddressSchema({ one = "address", title = "Address", ...options }: AddressSchemaOptions = {})Schema that defines a valid color hex string, e.g. #00CCFF
new ColorSchema({ one = "color", title = "Color", value = "#000000", input = "color", max = 7, ...options }: ColorSchemaOptions)Schema that validates a data object against a fixed set of named property schemas.
new DataSchema<T>({ one = "item", title = "Item", props, value: partialValue, ...options }: DataSchemaOptions<T>)Schema representing a numeric amount in a specific currency.
new CurrencyAmountSchema({ currency, one = "amount", title = "Amount", symbol, step, ...options }: CurrencyAmountSchemaOptions)Schema that validates an array and ensures every item matches a specified item schema.
new ArraySchema<T>({
items,
one = items.one,
many = items.many,
title = "Items",
placeholder = `No ${many}`,
unique = false,
min = 0,
max = Number.POSITIVE_INFINITY,
separator = ",",
value = [],
...options
}: ArraySchemaOptions<T>)Schema that defines a valid boolean.
new BooleanSchema({ value = false, required = false, ...options }: BooleanSchemaOptions)Schema that defines a valid phone number.
new PhoneSchema({ one = "phone number", title = "Phone", input = "tel", max = 16, ...options }: PhoneSchemaOptions)Schema that defines a valid absolute URI string.
new URISchema({ one = "URI", title = "URI", schemes = HTTP_SCHEMES, input = "url", max = 512, ...options }: URISchemaOptions)Schema that defines a valid UUID string (versions 1-5). Defaults to any-version validation.
new UUIDSchema({ one = "UUID", title = "UUID", max = 36, ...rest }: UUIDSchemaOptions = {})Schema that validates a dictionary object whose entries all share the same value schema and have string keys.
new DictionarySchema<T>({
items,
one = items.one,
many = items.many,
placeholder = `No ${many}`,
min = 0,
max = Number.POSITIVE_INFINITY,
title = "Items",
value = {},
...options
}: DictionarySchemaOptions<T>)Schema that defines a valid date stored as a YYYY-MM-DD string, e.g. 2005-09-12.
new DateSchema({ one = "date", min, max, value, input = "date", step, ...options }: DateSchemaOptions)Schema that validates an Entity string combining a type and ID, e.g. challenge:a1b2c3.
new EntitySchema<T>({ one = "entity", title = "Entity", types, ...options }: EntitySchemaOptions<T>)Schema that wraps a source schema and additionally allows null.
new NullableSchema<T>({ value = null, ...options }: NullableSchemaOptions<T>)Schema that defines a valid email address.
new EmailSchema({ one = "email address", title = "Email", input = "email", max = 254, ...options }: EmailSchemaOptions)Schema that wraps and passes through to a source schema, used as a base for schemas that augment another schema's behaviour (e.g. NullableSchema, OptionalSchema, RequiredSchema).
new ThroughSchema<T>({ source, ...options }: ThroughSchemaOptions<T>)Schema that defines a valid slug, e.g. this-is-a-slug.
new SlugSchema({ max = 32, ...options }: SlugSchemaOptions)Schema that defines a valid password string.
new PasswordSchema({ one = "password", title = "Password", min = 6, input = "password", ...options }: StringSchemaOptions = {})Schema that defines a valid ISO 3166 country code, e.g. GB.
new CountrySchema({ one = "country", title = "Country", value = "detect", ...options }: CountrySchemaOptions = {})Schema that validates a file name matching one or more extensions.
new FileSchema({ one = "file", title = "File", types, ...options }: FileSchemaOptions)Schema that defines a valid absolute or relative URL string.
new URLSchema({ one = "URL", title = "URL", base, schemes = HTTP_SCHEMES, input = "url", max = 512, ...options }: URLSchemaOptions)Schema that validates a database key string, stripping any non-alphanumeric characters.
new KeySchema({ one = "key", title = "Key", min = 1, max = 32, ...options }: StringSchemaOptions)Schema that wraps a source schema and additionally allows undefined.
new OptionalSchema<T>(options: OptionalSchemaOptions<T>)
Schema that validates a value against a fixed set of allowed string choices.
new ChoiceSchema<O, I>({ one = "choice", title = "Choice", placeholder = `No ${one}`, options, value, input, ...rest }: ChoiceSchemaOptions<O, I>)Schema that defines a valid abstract time in 24h hh:mm:ss.fff format, e.g. 23:59 or 24:00.
new TimeSchema({ one = "time", title = "Time", input = "time", ...options }: DateSchemaOptions)Schema that wraps a source schema but rejects a falsy result.
new RequiredSchema<T>()
Schema is an object instance with a validate() method that converts unknown input into a known, valid type T.
new Schema<T>({ one = "value", many = `${one}s`, title, description, placeholder, value }: SchemaOptions)Schema that validates the file name of an image.
new ImageSchema({ one = "image", title = "Image", types = IMAGE_TYPES, ...options }: MediaSchemaOptions)Schema that defines a valid number.
new NumberSchema({
one = "number",
title = "Number",
min = Number.NEGATIVE_INFINITY,
max = Number.POSITIVE_INFINITY,
step,
value,
...options
}: NumberSchemaOptions)Schema that validates the file name of an image or a video.
new MediaSchema({ one = "media", title = "Media", types = MEDIA_TYPES, capture, ...options }: MediaSchemaOptions)Schema that defines a valid ISO 4217 currency code, e.g. GBP.
new CurrencyCodeSchema({ one = "currency", title = "Currency", currencies = CURRENCY_CODES, max = 3, ...options }: CurrencyCodeSchemaOptions)Schema that defines a valid UTC datetime in ISO 8601 format, e.g. 2005-09-12T18:15:00.000Z.
new DateTimeSchema({ one = "time", title = "Time", input = "datetime-local", ...options }: DateSchemaOptions)Schema that validates the file name of a video.
new VideoSchema({ one = "video", title = "Video", types = VIDEO_TYPES, ...options }: MediaSchemaOptions)Options for StringSchema.
{
readonly value?: string | undefined;
readonly min?: number | undefined;
readonly max?: number | undefined;
readonly multiline?: boolean | undefined;
readonly rows?: number | undefined;
readonly match?: RegExp | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Allowed options for AddressSchema.
{
readonly value?: Partial<AddressData> | undefined;
}Options for a ColorSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Options for DataSchema.
{
readonly props: Schemas<T>;
readonly value?: Partial<T> | undefined;
}Options for CurrencyAmountSchema.
{
readonly symbol?: string | undefined;
readonly currency: CurrencyCode;
}Options for ArraySchema.
{
readonly value?: ImmutableArray;
readonly items: Schema<T>;
readonly min?: number;
readonly max?: number;
readonly unique?: boolean;
readonly separator?: string | RegExp;
}Allowed options for BooleanSchema.
{
readonly value?: boolean | undefined;
readonly required?: boolean | undefined;
}Options for a PhoneSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Options for URISchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly match?: RegExp | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
readonly schemes?: URISchemes | undefined;
}Options for a UUIDSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Options for DictionarySchema.
{
readonly items: Schema<T>;
readonly value?: ImmutableDictionary | undefined;
readonly min?: number | undefined;
readonly max?: number | undefined;
}Options for DateSchema.
{
readonly value?: PossibleDate | undefined;
readonly min?: Nullish<PossibleDate>;
readonly max?: Nullish<PossibleDate>;
readonly input?: DateInputType | undefined;
readonly step?: number | undefined;
}Options for EntitySchema.
{
readonly types?: ImmutableArray<T> | undefined;
}Allowed options for NullableSchema.
{
readonly value?: T | null;
}Options for an EmailSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Allowed options for ThroughSchema.
{
source: Schema<T>;
}Options for a SlugSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly match?: RegExp | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
}Options for CountrySchema.
{
readonly value?: PossibleCountry;
}Allowed options for FileSchema.
{
readonly types?: FileTypes | undefined;
}Options for URLSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly match?: RegExp | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
readonly base?: URL | URLString | undefined;
readonly schemes?: URISchemes | undefined;
}Allowed options for OptionalSchema.
{
readonly value?: undefined;
}Options for ChoiceSchema.
{
readonly options: PossibleChoiceOptions<O>;
readonly value?: O | I;
readonly input?: ChoiceInputType | undefined;
}Allowed options for NumberSchema.
{
readonly value?: number | undefined;
readonly min?: number | undefined;
readonly max?: number | undefined;
readonly step?: number | undefined;
}Allowed options for MediaSchema.
{
readonly capture?: FileCapture | undefined;
}Options for CurrencyCodeSchema.
{
readonly value?: string | undefined;
readonly max?: number | undefined;
readonly case?: "upper" | "lower" | undefined;
readonly input?: StringInputType | undefined;
readonly currencies?: ImmutableArray<CurrencyCode> | undefined;
}type="" prop for HTML <input /> tags that are relevant for strings.
"text" | "password" | "color" | "email" | "number" | "tel" | "search" | "url"
type="" prop for HTML <input /> tags that are relevant for dates.
"time" | "date" | "datetime-local"
Dictionary of allowed options for a ChoiceSchema in { key: title } format.
{ readonly [KK in K]: string }Things that can be converted to a ChoiceOptions dictionary.
ImmutableArray<K> | ChoiceOptions<K>
Input control hint for a ChoiceSchema: "radio" for radio inputs, or "select" for a <select>.
"radio" | "select"
Options allowed by a Schema instance.
{
readonly one?: string;
readonly many?: string;
readonly title?: string | undefined;
readonly description?: string | undefined;
readonly placeholder?: string | undefined;
readonly value?: unknown;
}Extract the validated value type T from a Schema<T>.
X extends Schema<infer Y> ? Y : never
A set of named schemas in { name: schema } format.
{ readonly [K in keyof T]: Schema<T[K]> }Sugar instance of StringSchema for an unconstrained string. Equivalent to new StringSchema({}).
Sugar instance of StringSchema requiring at least one character. Equivalent to new StringSchema({ min: 1 }).
Sugar instance of StringSchema for a title of 1–100 characters. Equivalent to new StringSchema({ one: "title", title: "Title", min: 1, max: 100 }).
Sugar instance allowing a TITLE or null. Equivalent to NULLABLE(TITLE).
Sugar instance of StringSchema for a name of 1–100 characters. Equivalent to new StringSchema({ one: "name", title: "Name", min: 1, max: 100 }).
Sugar instance allowing a NAME or null. Equivalent to NULLABLE(NAME).
Sugar instance of AddressSchema for postal address data. Equivalent to new AddressSchema({}).
Sugar instance allowing an ADDRESS or null. Equivalent to NULLABLE(ADDRESS).
Sugar instance of ColorSchema for a required hex color string, e.g. #00CCFF. Equivalent to new ColorSchema({}).
Sugar instance allowing a COLOR or null. Equivalent to NULLABLE(COLOR).
Sugar instance of CurrencyAmountSchema for a US dollar amount. Equivalent to new CurrencyAmountSchema({ currency: "USD" }).
Sugar instance of CurrencyAmountSchema for a pound sterling amount. Equivalent to new CurrencyAmountSchema({ currency: "GBP" }).
Sugar instance of CurrencyAmountSchema for a euro amount. Equivalent to new CurrencyAmountSchema({ currency: "EUR" }).
Sugar instance allowing a USD_AMOUNT or null. Equivalent to NULLABLE(USD_AMOUNT).
Sugar instance allowing a GBP_AMOUNT or null. Equivalent to NULLABLE(GBP_AMOUNT).
Sugar instance allowing an EUR_AMOUNT or null. Equivalent to NULLABLE(EUR_AMOUNT).
Sugar instance of BooleanSchema for a coerced boolean. Equivalent to new BooleanSchema({}).
Sugar instance of PhoneSchema for a valid phone number. Equivalent to new PhoneSchema({}).
Sugar instance allowing a PHONE or null. Equivalent to NULLABLE(PHONE).
Sugar instance of URISchema for an absolute URI string. Equivalent to new URISchema({}).
Sugar instance allowing a URI_SCHEMA or null. Equivalent to NULLABLE(URI_SCHEMA).
Sugar instance of UUIDSchema for any valid UUID (versions 1-5). Equivalent to new UUIDSchema({ title: "ID" }).
Sugar instance allowing a UUID or null. Equivalent to NULLABLE(UUID).
Sugar instance of DateSchema for a required date. Equivalent to new DateSchema({}).
Sugar instance allowing a DATE or null. Equivalent to NULLABLE(DATE).
Sugar instance of EntitySchema for an entity string, e.g. challenge:a1b2c3. Equivalent to new EntitySchema({}).
Sugar instance allowing an ENTITY or null. Equivalent to NULLABLE(ENTITY).
Sugar instance of EmailSchema for a valid email address. Equivalent to new EmailSchema({}).
Sugar instance allowing an EMAIL or null. Equivalent to NULLABLE(EMAIL).
Sugar instance of SlugSchema for a valid slug. Equivalent to new SlugSchema({}).
Sugar instance allowing a SLUG or null. Equivalent to NULLABLE(SLUG).
Sugar instance of PasswordSchema for a password string. Equivalent to new PasswordSchema({}).
Sugar instance of CountrySchema for a required ISO 3166 country code, e.g. GB. Equivalent to new CountrySchema({}).
Sugar instance allowing a COUNTRY or null. Equivalent to NULLABLE(COUNTRY).
Sugar instance of FileSchema for a file name, e.g. file.txt. Equivalent to new FileSchema({}).
Sugar instance allowing a FILE or null. Equivalent to NULLABLE(FILE).
Sugar instance of URLSchema for an absolute or relative URL string. Equivalent to new URLSchema({}).
Sugar instance allowing a URL_SCHEMA or null. Equivalent to NULLABLE(URL_SCHEMA).
Sugar instance of KeySchema for a database key. Equivalent to new KeySchema({ title: "ID" }).
Sugar instance allowing a KEY or null. Equivalent to NULLABLE(KEY).
Sugar instance of TimeSchema for a required abstract time. Equivalent to new TimeSchema({}).
Sugar instance allowing a TIME or null. Equivalent to NULLABLE(TIME).
Unknown validator that always passes through its input value as unknown.
UNKNOWN: Schema<unknown>
Undefined validator that always returns undefined.
UNDEFINED: Schema<undefined>
Null validator that always returns null.
NULL: Schema<null>
Sugar instance of ImageSchema for an image file name, e.g. photo.jpg. Equivalent to new ImageSchema({}).
Sugar instance allowing an IMAGE or null. Equivalent to NULLABLE(IMAGE).
Sugar instance of NumberSchema for an unconstrained number. Equivalent to new NumberSchema({ title: "Number" }).
Sugar instance allowing a NUMBER or null. Equivalent to NULLABLE(NUMBER).
Sugar instance of NumberSchema for an integer. Equivalent to new NumberSchema({ step: 1, min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER }).
Sugar instance of NumberSchema for a positive integer (excluding zero). Equivalent to new NumberSchema({ step: 1, min: 1, max: Number.MAX_SAFE_INTEGER }).
Sugar instance of NumberSchema for a non-negative integer (including zero). Equivalent to new NumberSchema({ step: 1, min: 0, max: Number.MAX_SAFE_INTEGER }).
Sugar instance of NumberSchema for a negative integer (excluding zero). Equivalent to new NumberSchema({ step: 1, min: Number.MIN_SAFE_INTEGER, max: -1 }).
Sugar instance of NumberSchema for a non-positive integer (including zero). Equivalent to new NumberSchema({ step: 1, min: Number.MIN_SAFE_INTEGER, max: 0 }).
Sugar instance allowing an INTEGER or null. Equivalent to NULLABLE(INTEGER).
Sugar instance of NumberSchema for a Unix timestamp (including milliseconds). Equivalent to new NumberSchema({ title: "Timestamp", step: 1, min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER }).
Sugar instance — alias of NULLABLE_INTEGER. Equivalent to NULLABLE_INTEGER.
Sugar instance of MediaSchema for an image or video file name, e.g. photo.jpg. Equivalent to new MediaSchema({}).
Sugar instance allowing a MEDIA or null. Equivalent to NULLABLE(MEDIA).
Sugar instance of CurrencyCodeSchema for a required ISO 4217 currency code, e.g. GBP. Equivalent to new CurrencyCodeSchema({}).
Sugar instance allowing a CURRENCY_CODE or null. Equivalent to NULLABLE(CURRENCY_CODE).
Sugar instance of DateTimeSchema for a required UTC datetime. Equivalent to new DateTimeSchema({}).
Sugar instance allowing a DATETIME or null. Equivalent to NULLABLE(DATETIME).
Sugar instance of VideoSchema for a video file name, e.g. clip.mp4. Equivalent to new VideoSchema({}).
Sugar instance allowing a VIDEO or null. Equivalent to NULLABLE(VIDEO).