Error Handling
Monadic error propagation and type-safe error patterns
/#
## Monadic Error Propagation
Type errors are values (MonadicError), not exceptions. They propagate
automatically through function chains — if any function receives an
error as input, it short-circuits and returns the error immediately.
No try/catch needed. No manual error checking between calls.
#/
// --- Error propagation through a pipeline ---
function validate(name: ''): '' {
return name.trim()
}
function greet(name: ''): 'Hello, !' {
return `Hello, ${name}!`
}
function shout(text: ''): '' {
return text.toUpperCase()
}
test 'valid input flows through the pipeline' {
// Each function's output feeds the next function's input
const result = shout(greet(validate('alice')))
expect(result).toBe('HELLO, ALICE!')
}
test 'type error propagates through the entire chain' {
// validate(42) returns a MonadicError (42 is not a string)
// greet() receives the error, short-circuits, returns it
// shout() receives the error, short-circuits, returns it
const result = shout(greet(validate(42)))
expect(result instanceof Error).toBe(true)
expect(result.message.includes('string')).toBe(true)
}
test 'error identity is preserved (same object, not a copy)' {
const err = validate(42)
expect(greet(err)).toBe(err)
expect(shout(err)).toBe(err)
}
// --- Result pattern for domain errors ---
function divide(a: 10, b: 2): { value: 5 } {
if (b === 0) {
return { value: NaN, error: 'Division by zero' }
}
return { value: a / b }
}
test 'divide handles zero' {
const result = divide(10, 0)
expect(result.error).toBe('Division by zero')
}
test 'divide works normally' {
const result = divide(10, 2)
expect(result.value).toBe(5)
}
// Errors propagate when passed as arguments to TJS functions.
// If you use a potentially-error value in a JS expression (e.g. result.length),
// check it first: if (result instanceof Error) return result
// In debug mode, errors include a callStack showing the full call chain.