Skip to content

arrayMemo()

ts
function arrayMemo<T>(parseItem): Parser<T[]>

Validate arrays

Type Parameters

Type Parameter
T

Parameters

ParameterTypeDescription
parseItemParser<T>

Returns

Parser<T[]>

a function that parses arrays


dictionaryMemo()

ts
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

ParameterTypeDescription
parseKeyParser<K>parses every key
parseValueParser<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:

ts
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:

ts
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()

ts
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.

ts
const isUser = lazy(() => objectCompiled({
 id: isNumber,
 name: isString,
})

Type Parameters

Type Parameter
T extends Record<string, unknown>

Parameters

ParameterTypeDescription
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()

ts
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

ParameterTypeDescription
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:

ts
const parseUser = object({
  id: parseNumber,
  active: parseBoolean,
  name: parseString,
  email: optional(parseString),
})

Annotate explicitly:

ts
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()

ts
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

Type Parameters

Type Parameter
T extends Record<string, unknown>

Parameters

ParameterTypeDescription
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()

ts
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:

ts
const handlePutUser = (body: unknown) => {
 const parseBody = object({
   id: parseNumber,
   name: parseString,
 })
}

A client decides to call it with an extra property email:

ts
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:

ts
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:

ts
const handlePutUser = (body: unknown) => {
 const parseBody = objectStrict({
   id: parseNumber,
   name: parseString,
 })
}

Type Parameters

Type Parameter
T extends Record<string, unknown>

Parameters

ParameterTypeDescription
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()

ts
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

ParameterTypeDescription
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:

ts
const parseVector2 = tuple([parseNumber, parseNumber])
parseVector2([12.5, 45.0]) // -> ParseSuccess<[number, number]>

Declare the type explicitly with type arguments:

ts
const parseVector2 = tuple<[number, number]>([parseNumber, parseNumber])
parseVector2([12.5, 45.0]) // -> ParseSuccess<[number, number]>

unionMemo()

ts
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

ParameterTypeDescription
...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:

ts
const parseNumberOrString = oneOf(parseNumber, parseString)
parseNumberOrString(0) // => ParseSuccess<number | string>
parseNumberOrString('abc') // => ParseSuccess<number | string>
parseNumberOrString(null) // => ParseError

Parse discriminated unions:

ts
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:

ts
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:

ts
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:

ts
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.