UNPKG

true-myth

Version:

A library for safe functional programming in JavaScript, with first-class support for TypeScript

267 lines (229 loc) 9.02 kB
/** Provides useful integrations between True Myth’s {@linkcode Result} and {@linkcode Task} types and any library that implements [Standard Schema][ss]. [ss]: https://standardschema.dev @module */ import Result from './result.js'; import Task from './task.js'; /** The result of parsing data with a synchronous Standard Schema-compliant schema. Just a {@linkcode Result} whose failure type is always a Standard Schema `FailureResult`. */ export type ParseResult<T> = Result<T, StandardSchemaV1.FailureResult>; /** A type to name a parser, most often used in conjunction with {@linkcode parserFor}. Helpful if you want to use a type you have written to constrain a Standard Schema-compatible schema, instead of creating the type from the schema. ## Example ```ts import { parserFor, type ParserFor } from 'true-myth/standard-schema'; import * as z from 'zod'; interface Person { name?: string | undefined; age: number; } const personParser: ParserFor<Person> = parserFor(z.object({ name: z.string().optional(), age: z.number().nonnegative(), })); ``` */ export type ParserFor<T> = (data: unknown) => ParseResult<T>; /** The result of parsing data with an asynchronous Standard Schema-compliant schema. Just a {@linkcode Task} whose rejection type is always a Standard Schema `FailureResult`. */ export type ParseTask<T> = Task<T, StandardSchemaV1.FailureResult>; /** A type to name an async parser, most often used in conjunction with {@linkcode asyncParserFor}. Helpful if you want to use a type you have written to constrain a Standard Schema-compatible async schema, instead of creating the type from the schema. ## Example ```ts import { parserFor, type ParserFor } from 'true-myth/standard-schema'; import { type } from 'arktype'; interface Person { name?: string | undefined; age: number; } const personParser: ParserFor<Person> = parserFor(type({ "name?": "string", age: "number>=0", })); ``` */ export type AsyncParserFor<T> = (data: unknown) => ParseTask<T>; /** Create a synchronous parser for unknown data to use with any library that implements Standard Schema (Zod, Arktype, Valibot, etc.). The resulting parser will accept `unknown` data and emit a {@linkcode Result}, which will be {@linkcode result.Ok Ok} if the schema successfully validates, or a {@linkcode result.Err Err} with the {@linkcode StandardSchemaV1.Issue Issue}s generated by the schema for invalid data. ## Examples Creating a parser with Zod: ```ts import { parserFor } from 'true-myth/standard-schema'; import * as z from 'zod'; interface Person { name?: string | undefined; age: number; } const parsePerson = parserFor(z.object({ name: z.string().optional(), age: z.number().nonnegative(), })); ``` Creating a parser with Arktype: ```ts import { parserFor } from 'true-myth/standard-schema'; import { type } from 'arktype'; interface Person { name?: string | undefined; age: number; } const parsePerson = parserFor(type({ 'name?': 'string', age: 'number>=0', })); ``` Other libraries work similarly! Once you have a parser, you can simply call it with any value and then use the normal {@linkcode Result} APIs. ```ts parsePerson({ name: "Old!", age: 112 }).match({ Ok: (person) => { console.log(`${person.name ?? "someone"} is ${person.age} years old.`); }, Err: (error) => { console.error("Something is wrong!", ...error.issues); } }); ``` ## Throws The parser created by `parserFor` will throw an {@linkcode InvalidAsyncSchema} error if the schema it was created from produces an async result, i.e., a `Promise`. Standard Schema is [currently unable][gh] to distinguish between synchronous and asynchronous parsers due to limitations in Zod. If you need to handle schemas which may throw, use {@linkcode asyncParserFor} instead. It will safely lift *all* results into a {@linkcode Task}, which you can then safely interact with asynchronously as usual. [gh]: https://github.com/standard-schema/standard-schema/issues/22 */ export declare function parserFor<S extends StandardSchemaV1>(schema: S): ParserFor<StandardSchemaV1.InferOutput<S>>; /** An error thrown when calling a parser created with `parserFor` produces a `Promise` instance. */ declare class InvalidAsyncSchema extends Error { readonly name = "InvalidAsyncSchema"; constructor(); } export type { InvalidAsyncSchema }; /** Create an asynchronous parser for unknown data to use with any library that implements Standard Schema (Zod, Arktype, Valibot, etc.). The resulting parser will accept `unknown` data and emit a {@linkcode Task}, which will be {@linkcode task.Resolved Resolved} if the schema successfully validates, or {@linkcode task.Rejected Rejected} with the {@linkcode StandardSchemaV1.Issue Issue}s generated by the schema for invalid data. If passed a parser that produces results synchronously, this function will lift it into a {@linkcode Task}. ## Examples With Zod: ```ts import { asyncParserFor } from 'true-myth/standard-schema'; import * as z from 'zod'; interface Person { name?: string | undefined; age: number; } const parsePerson = asyncParserFor(z.object({ name: z.optional(z.string()), // Define an async refinement so we have something to work with. This is a // placeholder for some kind of *real* async validation you might do! age: z.number().refine(async (val) => val >= 0), })); ``` Other libraries that support async validation or transformation work similarly (but not all libraries support this). Once you have a parser, you can simply call it with any value and then use the normal {@linkcode Task} APIs. ```ts await parsePerson({ name: "Old!", age: 112 }).match({ Resolved: (person) => { console.log(`${person.name ?? "someone"} is ${person.age} years old.`); }, Rejected: (error) => { console.error("Something is wrong!", ...error.issues); } }); ``` @param schema A Standard Schema-compatible schema that produces a result, possibly asynchronously. @returns A {@linkcode Task} that resolves to the output of the schema when it parses successfully and rejects with the `StandardSchema` `FailureResult` when it fails to parse. */ export declare function asyncParserFor<S extends StandardSchemaV1>(schema: S): AsyncParserFor<StandardSchemaV1.InferOutput<S>>; /** The Standard Schema interface. */ export interface StandardSchemaV1<Input = unknown, Output = Input> { /** The Standard Schema properties. */ readonly '~standard': StandardSchemaV1.Props<Input, Output>; } export declare namespace StandardSchemaV1 { /** The Standard Schema properties interface. */ interface Props<Input = unknown, Output = Input> { /** The version number of the standard. */ readonly version: 1; /** The vendor name of the schema library. */ readonly vendor: string; /** Validates unknown input values. */ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>; /** Inferred types associated with the schema. */ readonly types?: Types<Input, Output> | undefined; } /** The result interface of the validate function. */ type Result<Output> = SuccessResult<Output> | FailureResult; /** The result interface if validation succeeds. */ interface SuccessResult<Output> { /** The typed output value. */ readonly value: Output; /** The non-existent issues. */ readonly issues?: undefined; } /** The result interface if validation fails. */ interface FailureResult { /** The issues of failed validation. */ readonly issues: ReadonlyArray<Issue>; } /** The issue interface of the failure output. */ interface Issue { /** The error message of the issue. */ readonly message: string; /** The path of the issue, if any. */ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined; } /** The path segment interface of the issue. */ interface PathSegment { /** The key representing a path segment. */ readonly key: PropertyKey; } /** The Standard Schema types interface. */ interface Types<Input = unknown, Output = Input> { /** The input type of the schema. */ readonly input: Input; /** The output type of the schema. */ readonly output: Output; } /** Infers the input type of a Standard Schema. */ type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input']; /** Infers the output type of a Standard Schema. */ type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output']; } //# sourceMappingURL=standard-schema.d.ts.map