arrayMemo()
function arrayMemo<T>(parseItem): Parser<T[]>Validate arrays
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type | Description |
|---|---|---|
parseItem | Parser<T> |
Returns
Parser<T[]>
a function that parses arrays
dictionaryMemo()
function dictionaryMemo<K, V>(parseKey, parseValue): Parser<string extends K ? Record<K, V> : Partial<Record<K, V>>>Dictionaries are objects that map strings to other values.
Due to how TypeScript works, this function has two behaviors at the type level, depending on Key (At runtime, it always behaves the same):
- When the key is
string, it validatesRecord<string, V>. WhennoUncheckedIndexedAccessis enabled, TypeScript understands that a value retrieved with a string the value can beundefined. However, the value is not semantically identical toPartial<Record<string, V>>. - When the key is a subset of
string, it validatesPartial<Record<K, V>>. If the properties were not marked as optional, TypeScript would assume that all keys map to values.
Type Parameters
| Type Parameter |
|---|
K extends string |
V |
Parameters
| Parameter | Type | Description |
|---|---|---|
parseKey | Parser<K> | parses every key |
parseValue | Parser<V> | parses every value |
Returns
Parser<string extends K ? Record<K, V> : Partial<Record<K, V>>>
a parser for a record
Examples
Validate a dictionary:
const parseDictionary = dictionary(isString, isString)
parseDictionary({ hello: 'world' }) // -> Success
parseDictionary({ hello: 1 }) // -> FailureYou can transform the keys and values; for example, to only allow lowercase strings:
const parseLowerCase = (data: unknown): data is Lowercase<string> =>
typeof data === 'string' ? failure('Not a string') : success(data.toLowerCase())
const parseDictionary = dictionary(parseLowerCase, parseLowerCase)
parseDictionary({ hello: 'world' }) // -> Success<{ hello: 'world' }>
parseDictionary({ Hello: 'world' }) // -> Success<{ hello: 'world' }>
parseDictionary({ hello: 'World' }) // -> Success<{ hello: 'world' }>objectCompiledMemo()
function objectCompiledMemo<T>(schema): Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>Same as object, but performs just-in-time (JIT) compilation with the Function constructor, which greatly increases the execution speed of the validation. However, the JIT compilation is slow and gets executed at the time when the validation function is constructed. When invoking this function at the module level, it is recommended to wrap it in lazy to defer the JIT compilation to when the validation function is called for the first time. This function will be blocked in environments where the Function constructor is blocked; for example, when the Content-Security-Policy policy is set without the 'unsafe-eval' directive.
See
- object for a non-compiled version of this function.
- lazy for deferring the JIT compilation.
- objectStrictCompiled for a strict version.
- object for a non-just-in-time compiled version of this function.
Example
Defer the JIT compilation to when the validation function is called for the first time.
const isUser = lazy(() => objectCompiled({
id: isNumber,
name: isString,
})Type Parameters
| Type Parameter |
|---|
T extends Record<string, unknown> |
Parameters
| Parameter | Type | Description |
|---|---|---|
schema | { [K in string | number | symbol]-?: Object extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]> } | maps keys to validation functions. |
Returns
Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>
objectMemo()
function objectMemo<T>(schema): Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>Objects have a fixed set of properties of different types. If the data received has properties that are not declared in the parser, the extra properties are omitted from the result.
See
objectStrict for a strict version.
Type Parameters
| Type Parameter |
|---|
T extends Record<string, unknown> |
Parameters
| Parameter | Type | Description |
|---|---|---|
schema | { [K in string | number | symbol]-?: Object extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]> } | maps keys to validation functions. |
Returns
Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>
a parser function that validates objects according to schema.
Examples
Object with both required and optional properties:
const parseUser = object({
id: parseNumber,
active: parseBoolean,
name: parseString,
email: optional(parseString),
})Annotate explicitly:
type User = {
id: number
name?: string
}
const parseUser = object<User>({
id: parseNumber,
name: optional(parseString),
})Limitations
Optional unknown properties will be inferred as required. See Infer > limitations for in-depth information.
objectStrictCompiledMemo()
function objectStrictCompiledMemo<T>(schema): Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>Like objectCompiled, but fails when the input data object has undeclared properties in the same manner as objectStrict.
See
- objectStrict for a non-compiled version.
- objectCompiled for a non-strict version.
Type Parameters
| Type Parameter |
|---|
T extends Record<string, unknown> |
Parameters
| Parameter | Type | Description |
|---|---|---|
schema | { [K in string | number | symbol]-?: Object extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]> } |
Returns
Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>
objectStrictMemo()
function objectStrictMemo<T>(schema): Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>Like object, but fails when the input data object has undeclared properties. Although object removes undeclared properties from the result, there are scenarios where you want to reject the input if it has extra properties.
See
object for a non-strict version.
Example
When designing APIs, you want to reject calls to the API that includes undeclared properties, as this will allow you to add new properties in the future without breaking changes.
For example, consider a REST API endpoint PUT /user/:id, which is validating the body with the non-strict object parser:
const handlePutUser = (body: unknown) => {
const parseBody = object({
id: parseNumber,
name: parseString,
})
}A client decides to call it with an extra property email:
fetch('/user/1', {
method: 'PUT',
body: JSON.stringify({
id: 123,
name: 'Alice',
email: null
}),Since handlePutUser does not reject the API call, the client's success will succeed.
Now, the backend is updated to include the email property:
const handlePutUser = (body: unknown) => {
const parseBody = object({
id: parseNumber,
name: parseString,
email: optional(parseString),
})
}That is, email is optional, but not nullable. If the client now sends the same request, it will suddenly fail.
To avoid such breaking change, use objectStrict:
const handlePutUser = (body: unknown) => {
const parseBody = objectStrict({
id: parseNumber,
name: parseString,
})
}Type Parameters
| Type Parameter |
|---|
T extends Record<string, unknown> |
Parameters
| Parameter | Type | Description |
|---|---|---|
schema | { [K in string | number | symbol]-?: Object extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]> } |
Returns
Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>
tupleMemo()
function tupleMemo<T>(parsers): Parser<T>Construct parsers for tuples. If the data has more elements than expected, the extra elements are omitted from the result.
Type Parameters
| Type Parameter |
|---|
T extends readonly unknown[] |
Parameters
| Parameter | Type | Description |
|---|---|---|
parsers | [...{ [K in string | number | symbol]: Parser<T[K<K>]> }[]] | an array of parsers. Each parser validates the corresponding element in the data tuple. |
Returns
Parser<T>
a parser that validates tuples.
Examples
Parse 2D coordinates:
const parseVector2 = tuple([parseNumber, parseNumber])
parseVector2([12.5, 45.0]) // -> ParseSuccess<[number, number]>Declare the type explicitly with type arguments:
const parseVector2 = tuple<[number, number]>([parseNumber, parseNumber])
parseVector2([12.5, 45.0]) // -> ParseSuccess<[number, number]>unionMemo()
function unionMemo<T>(...parsers): Parser<T[number]>Executes parsers in order and returns the first successful parsing attempt, or a failure if all fail. Use it to parse unions, to parse data that comes in different shape, and to provide fallbacks for failed parsing attempts.
See
withDefault for a shorthand for fallback with a static value.
Type Parameters
| Type Parameter |
|---|
T extends readonly unknown[] |
Parameters
| Parameter | Type | Description |
|---|---|---|
...parsers | { [K in string | number | symbol]: Parser<T[K<K>]> } | A list of parsers to be called in order. |
Returns
Parser<T[number]>
A parser function that will call the parsers in parsers in order and return the result of the first successful parsing attempt, or a failure if all parsing attempts fail.
Examples
Parse unions:
const parseNumberOrString = oneOf(parseNumber, parseString)
parseNumberOrString(0) // => ParseSuccess<number | string>
parseNumberOrString('abc') // => ParseSuccess<number | string>
parseNumberOrString(null) // => ParseErrorParse discriminated unions:
const parseResult = oneOf(
object({
tag: equals('success')
value: parseString
}),
object({
tag: equals('error')
}),
)Provide fallbacks for failed parsing attempts; for example, parse a number from data that is either a number or a stringified number:
const parseStrOrNum = fallback([parseNumber, parseNumberFromString])
parseStrOrNum(2) // -> { tag: 'success', value: 2 }
parseStrOrNum('2') // -> { tag: 'success', value: 2 }(Do not encode data like this when there's a choice; but sometimes, existing data comes in weird shapes and forms.)
Provide a static default value when parsing fails:
const parseName = oneOf([
parseString,
() => success('Anonymous')
])You can also use withDefault for this use case.
When explicitly annotating oneOf, provide a tuple as type argument:
type Success = {
tag: 'success'
value: string
}
type Failure = {
tag: 'failure'
}
type Result = Success | Failure
const parseLogLevel = equals<[Success, Failure]>(
object({
tag: equals('success')
value: parseString
}),
object({
tag: equals('error')
}),
)Due to a limitation of TypeScript, it is not possible to write oneOf<string | number>(). Therefore, it is generally recommended to omit the type arguments for oneOf and let TypeScript infer them.
