FuncType - v0.45.0
    Preparing search index...

    Type Alias IOType

    IOType: <A extends unknown>(
        f: () => A | Promise<A>,
    ) => IOType<never, unknown, A> & {
        acquireRelease: <
            R extends unknown,
            E extends unknown,
            A extends unknown,
            B extends unknown,
        >(
            acquire: IOType<R, E, A>,
            use: (a: A) => IOType<R, E, B>,
            release: (a: A) => IOType<R, never, void>,
        ) => IOType<R, E, B>;
        all: <R extends unknown, E extends unknown, A extends unknown>(
            effects: readonly IOType<R, E, A>[],
        ) => IOType<R, E, readonly A[]>;
        any: <R extends unknown, E extends unknown, A extends unknown>(
            effects: readonly IOType<R, E, A>[],
        ) => IOType<R, E, A>;
        async: <A extends unknown>(
            f: () => Promise<A>,
        ) => IOType<never, unknown, A>;
        asyncResult: <D extends unknown, E extends unknown>(
            f: (signal?: AbortSignal) => Promise<Record<string, unknown>>,
            onThrow: (error: unknown) => E,
            config?: { dataKey?: string; errorKey?: string; signal?: AbortSignal },
        ) => IOType<never, E, Option<D>>;
        bracket: <
            R extends unknown,
            E extends unknown,
            A extends unknown,
            B extends unknown,
        >(
            acquire: IOType<R, E, A>,
            use: (a: A) => IOType<R, E, B>,
            release: (a: A) => IOType<R, never, void>,
        ) => IOType<R, E, B>;
        die: (defect: unknown) => IOType<never, never, never>;
        fail: <E extends unknown>(error: E) => IOType<never, E, never>;
        firstSuccessOf: <R extends unknown, E extends unknown, A extends unknown>(
            effects: readonly IOType<R, E, A>[],
        ) => IOType<R, E, A>;
        forEach: <
            R extends unknown,
            E extends unknown,
            A extends unknown,
            B extends unknown,
        >(
            items: readonly A[],
            f: (a: A) => IOType<R, E, B>,
        ) => IOType<R, E, readonly B[]>;
        forEachPar: <
            R extends unknown,
            E extends unknown,
            A extends unknown,
            B extends unknown,
        >(
            items: readonly A[],
            f: (a: A) => IOType<R, E, B>,
        ) => IOType<R, E, readonly B[]>;
        fromEither: <E extends unknown, A extends unknown>(
            either: Either<E, A>,
        ) => IOType<never, E, A>;
        fromNullable: <A extends unknown>(
            value: A | null | undefined,
        ) => IOType<never, void, A>;
        fromOption: <A extends unknown>(
            option: Option<A>,
        ) => IOType<never, void, A>;
        fromOptionOrFail: <E extends unknown, A extends unknown>(
            option: Option<A>,
            onNone: () => E,
        ) => IOType<never, E, A>;
        fromResult: <D extends unknown, E extends unknown>(
            result: { data: D | null; error: E | null },
        ) => IOType<never, E, Option<D>>;
        fromTry: <A extends unknown>(t: Try<A>) => IOType<never, Error, A>;
        gen: <A extends unknown>(
            f: () => Generator<IOType<any, any, any>, A, any>,
        ) => IOType<never, any, A>;
        interrupt: () => IOType<never, never, never>;
        liftPromise: <Args extends unknown[], A extends unknown>(
            f: (...args: Args) => Promise<A>,
        ) => (...args: Args) => IOType<never, unknown, A>;
        liftSync: <Args extends unknown[], A extends unknown>(
            f: (...args: Args) => A,
        ) => (...args: Args) => IOType<never, never, A>;
        never: <A extends unknown = never>() => IOType<never, never, A>;
        race: <R extends unknown, E extends unknown, A extends unknown>(
            effects: readonly IOType<R, E, A>[],
        ) => IOType<R, E, A>;
        service: <S extends unknown>(tag: TagType<S>) => IOType<S, never, S>;
        serviceWith: <S extends unknown, A extends unknown>(
            tag: TagType<S>,
            f: (service: S) => A,
        ) => IOType<S, never, A>;
        serviceWithIO: <
            S extends unknown,
            R extends unknown,
            E extends unknown,
            A extends unknown,
        >(
            tag: TagType<S>,
            f: (service: S) => IOType<R, E, A>,
        ) => IOType<S | R, E, A>;
        sleep: (ms: number) => IOType<never, never, void>;
        succeed: <A extends unknown>(value: A) => IOType<never, never, A>;
        sync: <A extends unknown>(f: () => A) => IOType<never, never, A>;
        timeout: <R extends unknown, E extends unknown, A extends unknown>(
            effect: IOType<R, E, A>,
            ms: number,
        ) => IOType<R, E | TimeoutError, A>;
        tryAsync: <A extends unknown, E extends unknown>(
            f: (signal?: AbortSignal) => Promise<A>,
            onError: (error: unknown) => E,
            signal?: AbortSignal,
        ) => IOType<never, E, A>;
        tryCatch: <A extends unknown, E extends unknown>(
            f: () => A,
            onError: (error: unknown) => E,
        ) => IOType<never, E, A>;
        tryPromise: <A extends unknown, E extends unknown>(
            opts: { catch: (error: unknown) => E; try: () => Promise<A> },
        ) => IOType<never, E, A>;
        withServices: <
            Services extends Record<string, TagType<unknown>>,
            A extends unknown,
        >(
            services: Services,
            f: (
                ctx: { [K in string | number | symbol]: TagService<Services[K]> },
            ) => A | Promise<A>,
        ) => IOType<TagService<Services[keyof Services]>, unknown, A>;
        get Do(): DoBuilder<never, never, Record<never, never>>;
        get unit(): IOType<never, never, void>;
    }

    IO effect type for lazy, composable effects with typed errors.

    Type Declaration

      • <A extends unknown>(f: () => A | Promise<A>): IOType<never, unknown, A>
      • IO constructor - creates an IO from a function. Automatically detects if the function returns a Promise and handles it appropriately.

        Type Parameters

        • A extends unknown

        Parameters

        • f: () => A | Promise<A>

        Returns IOType<never, unknown, A>

        // Sync usage
        const syncIO = IO(() => 42)

        // Async usage - auto-detected
        const asyncIO = IO(() => fetch('/api/data'))
    • acquireRelease: <R extends unknown, E extends unknown, A extends unknown, B extends unknown>(
          acquire: IOType<R, E, A>,
          use: (a: A) => IOType<R, E, B>,
          release: (a: A) => IOType<R, never, void>,
      ) => IOType<R, E, B>

      Alias for bracket with a more descriptive name.

    • all: <R extends unknown, E extends unknown, A extends unknown>(
          effects: readonly IOType<R, E, A>[],
      ) => IOType<R, E, readonly A[]>

      Runs all IOs in parallel and collects results.

    • any: <R extends unknown, E extends unknown, A extends unknown>(
          effects: readonly IOType<R, E, A>[],
      ) => IOType<R, E, A>

      Returns the first effect to succeed, or fails if all fail.

      const result = await IO.any([
      IO.fail("error1"),
      IO.succeed("success"),
      IO.fail("error2")
      ]).run() // "success"
    • async: <A extends unknown>(f: () => Promise<A>) => IOType<never, unknown, A>

      Creates an IO from an async thunk. The Promise is not created until the IO is run.

    • asyncResult: <D extends unknown, E extends unknown>(
          f: (signal?: AbortSignal) => Promise<Record<string, unknown>>,
          onThrow: (error: unknown) => E,
          config?: { dataKey?: string; errorKey?: string; signal?: AbortSignal },
      ) => IOType<never, E, Option<D>>

      Creates an IO from an async function that returns { data, error }. Handles both:

      • Thrown errors (mapped via onThrow)
      • Returned errors in the result object Supports cancellation via AbortSignal.

      This is the most ergonomic way to wrap Supabase and similar API calls.

      // Supabase query in one line:
      const getUser = (id: string): IO<never, Error, Option<User>> =>
      IO.asyncResult(
      () => supabase.from('users').select('*').eq('id', id).single(),
      toError
      )

      // With custom field names:
      const result = IO.asyncResult(
      () => customApi.fetch(),
      toError,
      { dataKey: 'result', errorKey: 'err' }
      )

      // With cancellation:
      const controller = new AbortController()
      const getUser = IO.asyncResult(
      (signal) => supabase.from('users').abortSignal(signal).select('*').single(),
      toError,
      { signal: controller.signal }
      )
      controller.abort() // Cancels the request
    • bracket: <R extends unknown, E extends unknown, A extends unknown, B extends unknown>(
          acquire: IOType<R, E, A>,
          use: (a: A) => IOType<R, E, B>,
          release: (a: A) => IOType<R, never, void>,
      ) => IOType<R, E, B>

      Ensures a resource is properly released after use. The release function always runs, even if use fails.

      const withFile = IO.bracket(
      IO.sync(() => openFile("data.txt")), // acquire
      file => IO.async(() => file.read()), // use
      file => IO.sync(() => file.close()) // release
      )
    • die: (defect: unknown) => IOType<never, never, never>

      Creates an IO that dies with an unrecoverable defect.

    • fail: <E extends unknown>(error: E) => IOType<never, E, never>

      Creates an IO that fails with the given error.

    • firstSuccessOf: <R extends unknown, E extends unknown, A extends unknown>(
          effects: readonly IOType<R, E, A>[],
      ) => IOType<R, E, A>

      Runs IOs in sequence, returning the first success or last failure.

    • forEach: <R extends unknown, E extends unknown, A extends unknown, B extends unknown>(
          items: readonly A[],
          f: (a: A) => IOType<R, E, B>,
      ) => IOType<R, E, readonly B[]>

      Executes an effect for each element in the array, collecting results.

      const results = await IO.forEach([1, 2, 3], n =>
      IO.sync(() => n * 2)
      ).run() // [2, 4, 6]
    • forEachPar: <R extends unknown, E extends unknown, A extends unknown, B extends unknown>(
          items: readonly A[],
          f: (a: A) => IOType<R, E, B>,
      ) => IOType<R, E, readonly B[]>

      Executes effects for each element in parallel (limited concurrency coming later). Alias for forEach.

    • fromEither: <E extends unknown, A extends unknown>(
          either: Either<E, A>,
      ) => IOType<never, E, A>

      Creates an IO from an Either.

    • fromNullable: <A extends unknown>(value: A | null | undefined) => IOType<never, void, A>

      Converts a nullable value to an IO.

    • fromOption: <A extends unknown>(option: Option<A>) => IOType<never, void, A>

      Creates an IO from an Option.

    • fromOptionOrFail: <E extends unknown, A extends unknown>(
          option: Option<A>,
          onNone: () => E,
      ) => IOType<never, E, A>

      Creates an IO from an Option with custom error.

    • fromResult: <D extends unknown, E extends unknown>(
          result: { data: D | null; error: E | null },
      ) => IOType<never, E, Option<D>>

      Creates an IO from a result object with data/error pattern. If error is present (truthy), fails with the error. Otherwise succeeds with Option-wrapped data (None if data is null/undefined).

      This handles the common { data, error } response pattern used by Supabase, many REST APIs, and similar libraries.

      const response = { data: user, error: null }
      const io = IO.fromResult(response) // IO<never, null, Option<User>> -> Some(user)

      const emptyResponse = { data: null, error: null }
      const emptyIo = IO.fromResult(emptyResponse) // IO<never, null, Option<User>> -> None

      const errorResponse = { data: null, error: new Error("Not found") }
      const failedIo = IO.fromResult(errorResponse) // IO<never, Error, Option<null>> -> fails
    • fromTry: <A extends unknown>(t: Try<A>) => IOType<never, Error, A>

      Creates an IO from a Try.

    • gen: <A extends unknown>(
          f: () => Generator<IOType<any, any, any>, A, any>,
      ) => IOType<never, any, A>

      Creates an IO from a generator function. This enables do-notation style programming.

      const program = IO.gen(function* () {
      const a = yield* IO.succeed(1)
      const b = yield* IO.succeed(2)
      return a + b
      })
    • interrupt: () => IOType<never, never, never>

      Creates an IO that is immediately interrupted.

    • liftPromise: <Args extends unknown[], A extends unknown>(
          f: (...args: Args) => Promise<A>,
      ) => (...args: Args) => IOType<never, unknown, A>

      Lifts a Promise-returning function into an IO-returning function.

    • liftSync: <Args extends unknown[], A extends unknown>(
          f: (...args: Args) => A,
      ) => (...args: Args) => IOType<never, never, A>

      Lifts a synchronous function into an IO-returning function.

    • never: <A extends unknown = never>() => IOType<never, never, A>

      Creates an IO that never completes.

    • race: <R extends unknown, E extends unknown, A extends unknown>(
          effects: readonly IOType<R, E, A>[],
      ) => IOType<R, E, A>

      Races multiple effects, returning the first to complete. Note: Other effects are NOT cancelled (JS limitation).

      const result = await IO.race([
      IO.sleep(1000).map(() => "slow"),
      IO.sleep(100).map(() => "fast")
      ]).run() // "fast"
    • service: <S extends unknown>(tag: TagType<S>) => IOType<S, never, S>

      Creates an IO that requires a service identified by the tag. The service must be provided before the effect can be run.

      interface Logger {
      log(message: string): void
      }
      const Logger = Tag<Logger>("Logger")

      const program = IO.service(Logger).flatMap(logger =>
      IO.sync(() => logger.log("Hello!"))
      )

      // Provide the service to run
      program.provideService(Logger, consoleLogger).run()
    • serviceWith: <S extends unknown, A extends unknown>(
          tag: TagType<S>,
          f: (service: S) => A,
      ) => IOType<S, never, A>

      Accesses a service and applies a function to it.

    • serviceWithIO: <S extends unknown, R extends unknown, E extends unknown, A extends unknown>(
          tag: TagType<S>,
          f: (service: S) => IOType<R, E, A>,
      ) => IOType<S | R, E, A>

      Accesses a service and applies an effectful function to it.

    • sleep: (ms: number) => IOType<never, never, void>

      Creates an IO that sleeps for the specified duration.

    • succeed: <A extends unknown>(value: A) => IOType<never, never, A>

      Creates an IO that succeeds with the given value.

    • sync: <A extends unknown>(f: () => A) => IOType<never, never, A>

      Creates an IO from a synchronous thunk. The function is not executed until the IO is run.

    • timeout: <R extends unknown, E extends unknown, A extends unknown>(
          effect: IOType<R, E, A>,
          ms: number,
      ) => IOType<R, E | TimeoutError, A>

      Creates a timeout effect that fails with TimeoutError.

    • tryAsync: <A extends unknown, E extends unknown>(
          f: (signal?: AbortSignal) => Promise<A>,
          onError: (error: unknown) => E,
          signal?: AbortSignal,
      ) => IOType<never, E, A>

      Creates an IO from an async thunk with typed error handling. Catches any thrown errors and maps them using the provided function. Supports cancellation via AbortSignal.

      This is a simpler alternative to tryPromise that takes a direct error mapper function instead of an options object.

      const io = IO.tryAsync(
      () => fetch('/api/users').then(r => r.json()),
      (e) => new ApiError(e)
      )

      // With cancellation:
      const controller = new AbortController()
      const io = IO.tryAsync(
      (signal) => fetch('/api/users', { signal }).then(r => r.json()),
      (e) => new ApiError(e),
      controller.signal
      )
      controller.abort() // Cancels the request
    • tryCatch: <A extends unknown, E extends unknown>(
          f: () => A,
          onError: (error: unknown) => E,
      ) => IOType<never, E, A>

      Creates an IO from a function that might throw.

    • tryPromise: <A extends unknown, E extends unknown>(
          opts: { catch: (error: unknown) => E; try: () => Promise<A> },
      ) => IOType<never, E, A>

      Creates an IO from a Promise with error handling.

    • withServices: <Services extends Record<string, TagType<unknown>>, A extends unknown>(
          services: Services,
          f: (
              ctx: { [K in string | number | symbol]: TagService<Services[K]> },
          ) => A | Promise<A>,
      ) => IOType<TagService<Services[keyof Services]>, unknown, A>

      Accesses multiple services and applies a function to them. Provides a convenient way to work with multiple dependencies.

      const program = IO.withServices(
      { logger: Logger, db: Database },
      ({ logger, db }) => {
      logger.log("Querying...")
      return db.query("SELECT * FROM users")
      }
      )
    • get Do(): DoBuilder<never, never, Record<never, never>>

      Starts a Do-builder context for binding values. This enables do-notation style programming without generators.

      const program = IO.Do
      .bind("user", () => getUser("123"))
      .bind("posts", ({ user }) => getPosts(user.id))
      .let("count", ({ posts }) => posts.length)
      .map(({ user, posts, count }) => ({ user, posts, count }))
    • get unit(): IOType<never, never, void>

      Creates a unit IO.

    // Basic usage
    const program = IO.sync(() => 42)
    .map(x => x * 2)
    .flatMap(x => IO.succeed(x + 1))

    const result = await program.run() // 85

    // Error handling
    const safe = IO.tryPromise({
    try: () => fetch('/api/data'),
    catch: (e) => new NetworkError(e)
    })
    .map(res => res.json())
    .recover({ fallback: 'default' })

    // Composition
    const composed = IO.all([
    IO.succeed(1),
    IO.succeed(2),
    IO.succeed(3)
    ]) // IO<never, never, [1, 2, 3]>