prefer-coded-error-result
Return a stable error code and typed context instead of only a rendered failure message.
Why
A message-only or opaque failure hides the identity needed for repair, conflict handling, localization and exhaustive dispatch. Preserve a finite error code and the minimum handling context; let the consumer format its message. Reuse the native no-explicit-any rule, compiler narrowing and type-aware exhaustiveness checks.
Fix
Use the existing Result or outcome union with a finite error code and variant-specific context. Preserve public behavior through a presentation adapter, and handle or propagate each relevant variant without parsing message text.
Examples
import type { Result as NativeResult } from "neverthrow";type Saved<T, E> = Promise<NativeResult<T, E>>;export type Output = Saved<number, string>;import type { Result as NativeResult } from "neverthrow";type Saved<T, E> = Promise<NativeResult<T, E>>;export type Output = Saved< number, { code: "duplicate"; name: string } | { code: "denied" }>;export type Result<T> = { ok: true; data: T } | { ok: false; message: string };type Error = { code: "duplicate"; name: string } | { code: "invalid_transition" };export type Result<T> = { ok: true; data: T } | { ok: false; error: Error };import { err, ok } from "neverthrow";export const save = (name: string, allowed: boolean) => allowed ? ok(name) : err(`Denied ${name}`);import { err, ok, type Result } from "neverthrow";type Denied = { code: "denied"; name: string };export function save(name: string, allowed: boolean): Result<string, Denied> { return allowed ? ok(name) : err({ code: "denied", name });}export function format(error: Denied): string { return `Denied ${error.name}`;}export function reject(name: string) { return { ok: false, code: "duplicate", message: `Duplicate ${name}` };}type Duplicate = { code: "duplicate"; name: string };export function reject(name: string): { ok: false; error: Duplicate } { return { ok: false, error: { code: "duplicate", name } };}export function format(error: Duplicate): string { return `Duplicate ${error.name}`;}export type Outcome<T> = { status: "success"; value: T } | { status: "failure"; reason: string };export type Source<T> = | { status: "loaded"; value: T } | { status: "failed" | "unconfigured"; reason: string };export type Outcome<T> = { ok: true; data: T } | { ok: false; error: string };export type Partial<T> = { ok: true; data: T; error?: string } | { ok: false; data: T; error: string };type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };export type Saved = Result<number, string>;type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };export type Saved = Result<number, "duplicate" | "invalid_transition">;import { err } from "neverthrow";export function reject(name: string) { return err(`Missing ${name}`);}import { err, type Result } from "neverthrow";type Missing = { code: "missing"; name: string };export function reject(name: string): Result<never, Missing> { return err({ code: "missing", name });}export type Outcome<T> = { ok: true; value: T } | { ok: false; fallbackError: string };export type Outcome<T> = | { ok: true; value: T } | { ok: false; fallbackError: { code: "unsupported"; requestedKind: string } };type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };export type Saved = Result<number, unknown>;type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };export type Saved = Result<number, { code: "unavailable" }>;export type Outcome<T> = { value: T } | { error: string; field?: "name" };type SaveError = { code: "duplicate"; name: string } | { code: "denied" };export type Outcome<T> = { value: T } | { error: SaveError };export type Outcome<T> = { kind: "ok"; value: T } | { kind: "error"; message: string };export type Outcome<T> = | { kind: "ok"; value: T } | { kind: "error"; error: { code: "duplicate"; name: string } };