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<Partial<Record<K, V>>>
Dictionaries are objects where all properties are optional and have the same type. In TypeScript, this can be represented by Partial<Record<string, ?>>
.
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
<Partial
<Record
<K
, V
>>>
a parser for a dictionary, of type Partial<Record<K, V>>
Examples
Validate a word dictionary:
const parseDictionary = dictionary(isString, isString)
parseDictionary({ hello: 'world' }) // -> Success
parseDictionary({ hello: 1 }) // -> Failure
You 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) // => ParseError
Parse 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.