carbonteq /FP

Typed Errors

Make failure explicit in types so callers handle errors intentionally.

The issue

When a function throws, the type signature usually only describes the "happy path". That means callers can't know (or be forced to handle) the failure modes without reading implementation details.

type  = { : string; : number };

class  extends  {}
class  extends  {}

// The signature only describes the happy path.
async function (: string, : number): <> {
  if (!) throw new ("userId required");
  if ( <= 0) throw new ("amount must be > 0");
  if ( > 100) throw new ("insufficient funds");
  return { : "PAY-123",  };
}

processPayment;
function processPayment(userId: string, amount: number): Promise<Payment>
async function () { try { const = await ("USER-1", 150); return ; } catch () { error;
function (local var) error: unknown
if ( instanceof ) return "Bad request"; if ( instanceof ) return "Payment declined"; return "Unknown error"; } } ();

What to prefer

Represent failure as a value (e.g. Result) so the failure path is explicit and composable:

import {  } from "@carbonteq/fp";

type  = { : string };
type  =
  | { : "ValidationError"; : string }
  | { : "InsufficientFunds"; : string };

function (: string, : number): <, > {
  if (!) return .({ : "ValidationError", : "userId required" });
  if ( <= 0) return .({ : "ValidationError", : "amount must be > 0" });
  if ( > 100) return .({ : "InsufficientFunds", : "insufficient funds" });
  return .({ : "PAY-123" });
}

const  = ("USER-1", 0);
res;
const res: Result<Payment, PaymentError>
const = .(() => .(`[PaymentError] ${.}: ${.}`)) .({ : () => `Paid: ${.}`, : () => ., }); message;
const message: string

Now the caller:

  • Sees the error type in the signature — no guessing what might throw
  • Is guided by the compilermatch requires handling both Ok and Err
  • Can log or transform errors inline with tapErr or mapErr

On this page