Skip to content

Adapters: switchFn, tee

Adapters — barrel export.

Re-exports adapter/interop functions for bridging between Result and other patterns.

Converts an Option to a Result, providing an error for the None case. Some(value) → Ok(value), None → Err(errorOnNone).

export function fromOption<E>(errorOnNone: E): <A>(opt: IOption<A>) => IResultOfT<A, E>;
export function fromOption<A, E>(errorOnNone: E, opt: IOption<A>): IResultOfT<A, E>;

Defined in: adapters/fromOption.ts

import { fromOption } from '@sandlada/result/adapters';
import { ofSome, ofNone } from '@sandlada/result/option';
import { match } from '@sandlada/result/operators';
const present = ofSome(42);
const okPath = match((n) => `ok:${n}`, (e) => `err:${String(e)}`, fromOption('missing', present));
// okPath === 'ok:42'; `opt` is Some(42) so `errorOnNone` is unused
const absent = ofNone<number>();
const errPath = match((n) => `ok:${n}`, (e) => `err:${String(e)}`, fromOption('missing', absent));
// errPath === 'err:missing'; None on `opt` becomes Err(errorOnNone)

Converts a one-track (plain) function into a switch function — lifts it to return a Result.

Optional errorFn (when supplied) maps the caught exception to a typed error; without it the error type defaults to unknown — mirrors tryCatch/fromPromise.

Wlaschin equivalent: succeed ∘ f

export function switchFn<A, B, E = unknown>(
f: (a: A) => B,
errorFn?: (error: unknown) => E,
): (a: A) => IResultOfT<B, E>;

Defined in: adapters/switchFn.ts

import { switchFn } from '@sandlada/result/adapters';
import { pipe } from '@sandlada/result/composition';
import { match } from '@sandlada/result/operators';
const safeDouble = switchFn((x: number) => x * 2);
const input = 21;
const label = pipe(safeDouble(input), match((n) => `ok:${n}`, (e) => `err:${String(e)}`));
// label === 'ok:42'; argument `a` receives 21 and the lifted fn returns Ok(42)
const unsafe = switchFn((_x: number) => { throw new Error('bad'); });
const badInput = 0;
const captured = pipe(unsafe(badInput), match((n) => `ok:${n}`, (e) => `err:${(e as Error).message}`));
// captured === 'err:bad'; a throw inside `f` is captured into Err

Converts a one-track async function into an async switch function — lifts it to return a Promise<IResultOfT>.

Optional errorFn (when supplied) maps the caught exception to a typed error; without it the error type defaults to unknown — mirrors tryCatchAsync/fromPromise.

export function switchFnAsync<A, B, E = unknown>(
f: (a: A) => B | Promise<B>,
errorFn?: (error: unknown) => E,
): (a: A) => Promise<IResultOfT<B, E>>;

Defined in: adapters/switchFnAsync.ts

import { switchFnAsync } from '@sandlada/result/adapters';
import { pipeAsync } from '@sandlada/result/composition';
import { matchAsync } from '@sandlada/result/promise-result';
const safeDouble = switchFnAsync(async (x: number) => x * 2);
const input = 21;
const label = await pipeAsync(
safeDouble(input),
matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`),
);
// label === 'ok:42'; argument `a` receives 21 and the async lift returns Ok(42)
const failing = switchFnAsync(async (_x: number) => Promise.reject(new Error('bad')));
const badInput = 0;
const captured = await pipeAsync(
failing(badInput),
matchAsync((n) => `ok:${n}`, (e) => `err:${(e as Error).message}`),
);
// captured === 'err:bad'; a rejected promise from `f` is captured into Err

Side-effect on the one-track — calls f and returns the value unchanged. Unlike tap (which operates on the success track of a Result), tee operates on a plain value outside the railway.

Throw policy: Unlike railway tap, tee operates on a plain value with no failure state, so a throwing f propagates. Ensure f does not throw.

Wlaschin equivalent: tee (dead-end function)

export function tee<A>(f: (a: A) => void): (a: A) => A;

Defined in: adapters/tee.ts

import { switchFn, tee } from '@sandlada/result/adapters';
import { pipe } from '@sandlada/result/composition';
import { match } from '@sandlada/result/operators';
const trace: number[] = [];
const input = 21;
const label = pipe(
input,
tee((n) => { trace.push(n); }),
switchFn((x: number) => x * 2),
match((n) => `ok:${n}`, (e) => `err:${String(e)}`),
);
// label === 'ok:42' and trace === [21]; `a` passes through tee before switchFn lifts
const afterTee = pipe(input, tee((_n: number) => { void _n; }));
const failed = switchFn((_x: number) => { throw new Error('bad'); })(afterTee);
const captured = match((n) => `ok:${n}`, (e) => `err:${(e as Error).message}`, failed);
// captured === 'err:bad'; tee still forwards `a` — the throw is captured downstream

Async side-effect on the one-track — calls f and returns the value unchanged.

Throw policy: Unlike railway tap, teeAsync operates on a plain value with no failure state, so a throwing (or rejecting) f propagates. Ensure f does not throw.

export function teeAsync<A>(f: (a: A) => void | Promise<void>): (a: A) => Promise<A>;

Defined in: adapters/teeAsync.ts

import { switchFnAsync, teeAsync } from '@sandlada/result/adapters';
import { matchAsync } from '@sandlada/result/promise-result';
const trace: number[] = [];
const input: number = 21;
const afterSideEffect = teeAsync(async (n: number) => { trace.push(n); });
const lifted = switchFnAsync(async (x: number) => x * 2);
const label = await matchAsync(
(n) => `ok:${n}`,
(e) => `err:${String(e)}`,
lifted(await afterSideEffect(input)),
);
// label === 'ok:42' and trace === [21]; `a` is unchanged after the async side effect
const afterTee = await teeAsync(async (_n: number) => { void _n; })(input);
const failing = switchFnAsync(async (_x: number) => Promise.reject(new Error('bad')));
const captured = await matchAsync(
(n) => `ok:${n}`,
(e) => `err:${(e as Error).message}`,
failing(afterTee),
);
// captured === 'err:bad'; teeAsync forwards `a` — rejection is captured by switchFnAsync

Converts a Result to an Option. Ok(value) → Some(value), Err(_) → None. Discards the error information.

export function toOption<A, E>(r: IResultOfT<A, E>): IOption<A>;

Defined in: adapters/toOption.ts

import { toOption } from '@sandlada/result/adapters';
import { ok, err } from '@sandlada/result/factories';
import { match } from '@sandlada/result/option';
const successInput = ok(42);
const somePath = match((n) => `some:${n}`, () => 'none', toOption(successInput));
// somePath === 'some:42'; `r` receives Ok(42) and becomes Some(42)
const failureInput = err('boom');
const nonePath = match((n) => `some:${n}`, () => 'none', toOption(failureInput));
// nonePath === 'none'; Err on `r` discards the error and becomes None