wellcrafted
Version:
Delightful TypeScript patterns for elegant, type-safe applications
184 lines (182 loc) • 6.26 kB
TypeScript
import { Err } from "../result-fCtNge01.js";
//#region src/error/types.d.ts
/**
* Base error structure for all errors in the Result system.
*
* Provides a consistent structure for error objects that are JSON-serializable
* and contain comprehensive debugging information.
*
* @example
* ```ts
* const error: BaseError = {
* name: "DatabaseError",
* message: "Connection failed",
* context: { host: "localhost", port: 5432 },
* cause: originalError
* };
* ```
*/
type BaseError = Readonly<{
name: string;
message: string;
context?: Record<string, unknown>;
cause: unknown;
}>;
/**
* Creates a tagged error type for type-safe error handling.
* Uses the `name` property as a discriminator for tagged unions.
*
* Error types should follow the convention of ending with "Error" suffix.
* The Err data structure wraps these error types in the Result system.
*
* @example
* ```ts
* type ValidationError = TaggedError<"ValidationError">
* type NetworkError = TaggedError<"NetworkError">
*
* function handleError(error: ValidationError | NetworkError) {
* switch (error.name) {
* case "ValidationError": // TypeScript knows this is ValidationError
* break;
* case "NetworkError": // TypeScript knows this is NetworkError
* break;
* }
* }
*
* // Used in Result types:
* function validate(input: string): Result<string, ValidationError> {
* if (!input) {
* return Err({
* name: "ValidationError",
* message: "Input is required",
* context: { input },
* cause: null,
* });
* }
* return Ok(input);
* }
*
* // Context is optional - omit when not needed:
* function checkAuth(): Result<User, AuthError> {
* if (!isAuthenticated) {
* return Err({
* name: "AuthError",
* message: "User not authenticated",
* cause: null,
* });
* }
* return Ok(currentUser);
* }
* ```
*/
type TaggedError<T extends string> = BaseError & {
readonly name: T;
};
//# sourceMappingURL=types.d.ts.map
//#endregion
//#region src/error/utils.d.ts
/**
* Extracts a readable error message from an unknown error value
*
* This utility is commonly used in mapErr functions when converting
* unknown errors to typed error objects in the Result system.
*
* @param error - The unknown error to extract a message from
* @returns A string representation of the error
*
* @example
* ```ts
* // With native Error
* const error = new Error("Something went wrong");
* const message = extractErrorMessage(error); // "Something went wrong"
*
* // With string error
* const stringError = "String error";
* const message2 = extractErrorMessage(stringError); // "String error"
*
* // With object error
* const unknownError = { code: 500, details: "Server error" };
* const message3 = extractErrorMessage(unknownError); // '{"code":500,"details":"Server error"}'
*
* // Used in mapErr function
* const result = await tryAsync({
* try: () => riskyOperation(),
* mapErr: (error) => Err({
* name: "NetworkError",
* message: extractErrorMessage(error),
* context: { operation: "riskyOperation" },
* cause: error,
* }),
* });
* ```
*/
declare function extractErrorMessage(error: unknown): string;
/**
* Input type for creating a tagged error (everything except the name)
*/
type TaggedErrorWithoutName<T extends string> = Omit<TaggedError<T>, "name">;
/**
* Replaces the "Error" suffix with "Err" suffix in error type names.
*
* This utility type is used to create companion function names for error constructors
* that return Err-wrapped results, maintaining consistent naming conventions.
*
* @template T - An error type name that must end with "Error"
* @returns The type name with "Error" replaced by "Err"
*
* @example
* ```ts
* type NetworkErr = ReplaceErrorWithErr<"NetworkError">; // "NetworkErr"
* type ValidationErr = ReplaceErrorWithErr<"ValidationError">; // "ValidationErr"
* type AuthErr = ReplaceErrorWithErr<"AuthError">; // "AuthErr"
* ```
*/
type ReplaceErrorWithErr<T extends `${string}Error`> = T extends `${infer TBase}Error` ? `${TBase}Err` : never;
/**
* Return type for createTaggedError - contains both factory functions.
*
* Provides two factory functions:
* - One that creates plain TaggedError objects (named with "Error" suffix)
* - One that creates Err-wrapped TaggedError objects (named with "Err" suffix)
*/
type TaggedErrorFactories<TErrorName extends `${string}Error`> = { [K in TErrorName]: (input: TaggedErrorWithoutName<K>) => TaggedError<K> } & { [K in ReplaceErrorWithErr<TErrorName>]: (input: TaggedErrorWithoutName<TErrorName>) => Err<TaggedError<TErrorName>> };
/**
* Returns two different factory functions for tagged errors.
*
* Given an error name like "NetworkError", this returns:
* - `NetworkError`: Creates a plain TaggedError object
* - `NetworkErr`: Creates a TaggedError object wrapped in an Err result
*
* The naming pattern automatically replaces the "Error" suffix with "Err" suffix
* for the Result-wrapped version.
*
* @param name - The name of the error type (must end with "Error")
* @returns An object with two factory functions:
* - [name]: Function that creates plain TaggedError objects
* - [name with "Error" replaced by "Err"]: Function that creates Err-wrapped TaggedError objects
*
* @example
* ```ts
* const { NetworkError, NetworkErr } = createTaggedError('NetworkError');
*
* // NetworkError: Creates just the error object
* const error = NetworkError({
* message: 'Connection failed',
* context: { url: 'https://api.example.com' },
* cause: undefined
* });
* // Returns: { name: 'NetworkError', message: 'Connection failed', ... }
*
* // NetworkErr: Creates error and wraps in Err result
* return NetworkErr({
* message: 'Connection failed',
* context: { url: 'https://api.example.com' },
* cause: undefined
* });
* // Returns: Err({ name: 'NetworkError', message: 'Connection failed', ... })
* ```
*/
declare function createTaggedError<TErrorName extends `${string}Error`>(name: TErrorName): TaggedErrorFactories<TErrorName>;
//#endregion
export { BaseError, TaggedError, createTaggedError, extractErrorMessage };
//# sourceMappingURL=index.d.ts.map