pure-parse
Version:
Strongly typed validation library that decouples type aliases from validation logic
119 lines (118 loc) • 5.26 kB
TypeScript
import { OptionalParser, Parser } from './Parser';
import { OptionalKeys, WithOptionalFields } from '../internals/utility-types';
/**
* 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 {@link objectStrict} for a strict version.
* @example
* Object with both required and optional properties:
* ```ts
* const parseUser = object({
* id: parseNumber,
* active: parseBoolean,
* name: parseString,
* email: optional(parseString),
* })
* ```
* @example
* 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 {@link Infer} > limitations for in-depth information.
* @param schema maps keys to validation functions.
* @return a parser function that validates objects according to `schema`.
*/
export declare const object: <T extends Record<string, unknown>>(schema: { [K in keyof T]-?: {} extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]>; }) => Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>;
/**
* Same as {@link 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 {@link 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) policy is set without the `'unsafe-eval`' directive.
* @see {@link object} for a non-compiled version of this function.
* @see {@link lazy} for deferring the JIT compilation.
* @see {@link objectStrictCompiled} for a strict version.
* @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,
* })
* ```
* @see {@link object} for a non-just-in-time compiled version of this function.
* @param schema maps keys to validation functions.
*/
export declare const objectCompiled: <T extends Record<string, unknown>>(schema: { [K in keyof T]-?: {} extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]>; }) => 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 {@link 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,
* })
* }
* ```
* @param schema
*/
export declare const objectStrict: <T extends Record<string, unknown>>(schema: { [K in keyof T]-?: {} extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]>; }) => 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 {@link objectStrict} for a non-compiled version.
* @see {@link objectCompiled} for a non-strict version.
* @param schema
*/
export declare const objectStrictCompiled: <T extends Record<string, unknown>>(schema: { [K in keyof T]-?: {} extends Pick<T, K> ? OptionalParser<T[K]> : Parser<T[K]>; }) => Parser<OptionalKeys<T> extends undefined ? T : WithOptionalFields<T>>;