@shirudo/base-error
Version:
A simple error base class for TypeScript
105 lines (102 loc) • 4.2 kB
text/typescript
/**
* Application-specific base error that works across full Node.js, isolate "edge"
* runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge Functions) and modern
* browsers. It preserves the native `cause` field where available, falls back
* gracefully where it is not, and produces the richest stack trace the host
* can provide.
*
* This class includes support for default and localized user-friendly messages.
*
* @example
* ```ts
* // Using automatic name inference
* class UserNotFoundError extends BaseError<'UserNotFoundError'> {
* constructor(userId: string) {
* super(`User with id ${userId} not found in database lookup`); // Technical message
* this.withUserMessage(`User ${userId} was not found.`); // User-friendly message
* }
* }
* ```
*/
declare class BaseError<T extends string> extends Error {
#private;
readonly name: T;
/** Epoch-ms timestamp (numeric) */
readonly timestamp: number;
/** ISO-8601 timestamp (string) for log aggregators that prefer text */
readonly timestampIso: string;
/** Rich, filtered stack where the host supports it. */
readonly stack?: string;
private _defaultUserMessage?;
private _localizedMessages;
/**
* Creates a new BaseError instance with automatic name inference.
*
* @param message – Human-readable explanation (name will be inferred from constructor)
* @param cause – Optional underlying error or extra context
*/
constructor(message: string, cause?: unknown);
/**
* Sets the default user-friendly message.
* This is used as a fallback when a specific localization is not available.
* @param message The default user-friendly message (typically in English).
* @returns The error instance for chaining.
*/
withUserMessage(message: string): this;
/**
* Adds a user-friendly message for a specific language.
* Throws an error if a message for the given language already exists.
* @param lang The language code (e.g., 'de', 'es', 'fr-CA').
* @param message The localized message.
* @returns The error instance for chaining.
* @throws Error if a message for the given language already exists.
*/
addLocalizedMessage(lang: string, message: string): this;
/**
* Updates or sets a user-friendly message for a specific language.
* This method allows overwriting existing messages for the same language.
* @param lang The language code (e.g., 'de', 'es', 'fr-CA').
* @param message The localized message.
* @returns The error instance for chaining.
*/
updateLocalizedMessage(lang: string, message: string): this;
/**
* Retrieves the most appropriate user-friendly message based on language preference.
* The fallback order is: preferred language -> fallback language -> default message.
* @param options - Language preference options.
* @returns The user-friendly message, or `undefined` if none is set.
*/
getUserMessage(options?: {
preferredLang?: string;
fallbackLang?: string;
}): string | undefined;
/** Serialises the error for JSON logs */
toJSON(): Record<string, unknown>;
/** Readable one-liner plus optional nested cause. */
toString(): string;
}
/**
* Asserts that a condition is truthy, throwing the provided error if it's falsy.
* This function provides TypeScript type narrowing through assertion signatures.
*
* @template T - The error name type extending string
* @param condition - The value to check for truthiness
* @param error - The BaseError instance to throw if condition is falsy
* @throws {BaseError<T>} The provided error when condition is falsy
*
* @example
* ```ts
* const user = getUser();
* guard(user, new UserNotFoundError("User not found"));
* // TypeScript now knows user is not null/undefined
* console.log(user.name);
* ```
*
* @example
* ```ts
* guard(isValidEmail(email), new ValidationError("Invalid email format"));
* // Continues execution only if email is valid
* ```
*/
declare function guard<T extends string>(condition: unknown, error: BaseError<T>): asserts condition;
export { BaseError, guard };