UNPKG

pure-parse

Version:

Strongly typed validation library that decouples type aliases from validation logic

119 lines (118 loc) 5.26 kB
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>>;