From 7599e18208118c6a2f0e078733cfa03cfed8d8ed Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Thu, 15 Aug 2024 17:00:20 -0400 Subject: [PATCH] feat(types): add Ok/Err/Result/ResultPromise types --- src/types.ts | 241 ++++++++++----------------------------------------- 1 file changed, 47 insertions(+), 194 deletions(-) diff --git a/src/types.ts b/src/types.ts index 234204254..247d21a29 100644 --- a/src/types.ts +++ b/src/types.ts @@ -98,214 +98,67 @@ export type Intersect = (U extends any ? (k: U) => void : never) extends ( export type Simplify = {} & { [P in keyof T]: T[P] } /** - * Get all properties **not using** the `?:` type operator. - */ -export type RequiredKeys = T extends any - ? keyof T extends infer K - ? K extends keyof T - ? Omit extends T - ? never - : K - : never - : never - : never - -/** - * Get all properties using the `?:` type operator. - */ -export type OptionalKeys = T extends any - ? keyof T extends infer K - ? K extends keyof T - ? Omit extends T - ? K - : never - : never - : never - : never - -/** - * Resolves to `true` if `Left` and `Right` are exactly the same type. + * A result tuple where the error is `undefined`. * - * Otherwise false. + * @example + * ```ts + * type GoodResult = Ok + * // ^? [undefined, string] + * ``` */ -export type IsExactType = [Left] extends [Any] - ? [Right] extends [Any] - ? true - : false - : (() => U extends Left ? 1 : 0) extends () => U extends Right ? 1 : 0 - ? true - : false - -export type Primitive = - | number - | string - | boolean - | symbol - | bigint - | null - | undefined - | void +export type Ok = [err: undefined, result: TResult] /** - * Coerce a primitive type to its boxed equivalent. + * A result tuple where an error is included. + * + * Note that `TError` is non-nullable, which means that + * `Err` and `Err` are not valid. * * @example * ```ts - * type A = BoxedPrimitive - * // ^? String - * type B = BoxedPrimitive - * // ^? Number + * type BadResult = Err + * // ^? [Error, undefined] + * + * type BadResult2 = Err + * // ^? [TypeError | MyCoolCustomError, undefined] * ``` */ -export type BoxedPrimitive = T extends string - ? // biome-ignore lint: - String - : T extends number - ? // biome-ignore lint: - Number - : T extends boolean - ? // biome-ignore lint: - Boolean - : T extends bigint - ? // biome-ignore lint: - BigInt - : T extends symbol - ? // biome-ignore lint: - Symbol - : never - -export type TypedArray = - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array - | BigInt64Array - | BigUint64Array - | DataView - | ArrayBuffer - | SharedArrayBuffer +export type Err = [err: NonNullable, result: undefined] /** - * Add your own classes to this regitsry by extending its interface - * with what's called “declaration merging” in TypeScript. + * A result tuple. * - * All property types in this registry type may be treated specially - * by any of Radashi's complex types. For example, `assign` will avoid - * merging with types in this registry. - */ -// biome-ignore lint: Preserve `interface` type. -export interface CustomClassRegistry {} - -/** - * This type represents any custom class that was "registered" through - * the `CustomClassRegistry` type. + * First index is the error, second index is the result. + * + * @example + * ```ts + * type MyResult = Result + * // ^? Ok | Err + * + * type MyResult2 = Result + * // ^? Ok | Err + * ``` */ -export type CustomClass = CustomClassRegistry[keyof CustomClassRegistry] +export type Result = + | Ok + | Err> /** - * These types are implemented natively. + * A promise that resolves to a result tuple. * - * Note that boxed primitives like `Boolean` (different from - * `boolean`) are not included, because `boolean extends Boolean ? 1 : - * 0` resolves to 1. + * @example + * ```ts + * type MyResult = ResultPromise + * // ^? Promise | Err> + * + * type MyResult2 = ResultPromise + * // ^? Promise | Err> + * ``` */ -export type BuiltInType = - | ES2021.BuiltInType - | WebAPI.BuiltInType - | NodeJS.BuiltInType - -// Start at ES2020, since they are the typings used by Radashi. -declare namespace ES2020 { - // Note: Don't include subtypes of types already listed here. - type BuiltInType = - | Primitive - | Promise - | Date - | RegExp - | Error - | readonly any[] - | ReadonlyMap - | ReadonlySet - | WeakMap - | WeakSet - | TypedArray - // biome-ignore lint: Support the Function type. - | Function -} - -declare namespace ES2021 { - // Note: Don't include subtypes of types already listed here. - type BuiltInType = - | ES2020.BuiltInType - | GlobalObjectType<'FinalizationRegistry'> - | GlobalObjectType<'WeakRef'> -} - -declare namespace NodeJS { - type BuiltInType = GlobalObjectType<'Buffer'> -} - -declare namespace WebAPI { - // Note: Don't include subtypes of types already listed here. - type BuiltInType = - | GlobalObjectType<'AbortController'> - | GlobalObjectType<'AbortSignal'> - | GlobalObjectType<'Blob'> - | GlobalObjectType<'Body'> - | GlobalObjectType<'CompressionStream'> - | GlobalObjectType<'Crypto'> - | GlobalObjectType<'CustomEvent'> - | GlobalObjectType<'DecompressionStream'> - | GlobalObjectType<'Event'> - | GlobalObjectType<'EventTarget'> // <-- Watch out for subtypes of this. - | GlobalObjectType<'FormData'> - | GlobalObjectType<'Headers'> - | GlobalObjectType<'MessageChannel'> - | GlobalObjectType<'Navigator'> - | GlobalObjectType<'ReadableStream'> - | GlobalObjectType<'ReadableStreamBYOBReader'> - | GlobalObjectType<'ReadableStreamDefaultController'> - | GlobalObjectType<'ReadableStreamDefaultReader'> - | GlobalObjectType<'SubtleCrypto'> - | GlobalObjectType<'TextDecoder'> - | GlobalObjectType<'TextDecoderStream'> - | GlobalObjectType<'TextEncoder'> - | GlobalObjectType<'TextEncoderStream'> - | GlobalObjectType<'TransformStream'> - | GlobalObjectType<'TransformStreamDefaultController'> - | GlobalObjectType<'URL'> - | GlobalObjectType<'URLSearchParams'> - | GlobalObjectType<'WebSocket'> - | GlobalObjectType<'WritableStream'> - | GlobalObjectType<'WritableStreamDefaultController'> - | GlobalObjectType<'WritableStreamDefaultWriter'> - | WebDocumentAPI.BuiltInType -} - -declare namespace WebDocumentAPI { - // Note: Don't include subtypes of types already listed here. - type BuiltInType = - | GlobalObjectType<'Node'> - | GlobalObjectType<'NodeList'> - | GlobalObjectType<'NodeIterator'> - | GlobalObjectType<'HTMLCollection'> - | GlobalObjectType<'CSSStyleDeclaration'> - | GlobalObjectType<'DOMStringList'> - | GlobalObjectType<'DOMTokenList'> -} - -// Infer an object type from a global constructor that is -// environment-specific. This helps avoid including unsupported types -// (according to tsconfig "lib" property), which can break things. -type GlobalObjectType = [Identifier] extends [Any] - ? never - : keyof Identifier extends never - ? never - : typeof globalThis extends { [P in Identifier]: any } - ? InstanceType<(typeof globalThis)[Identifier]> - : never +export type ResultPromise = Promise< + [NonNullable] extends [never] + ? Ok + : [TResult] extends [never] + ? Err> + : Result> +>