Effection Logo

TypeScript

Effection is written in TypeScript and comes bundled with its own type definitions. As such, it doesn't require any special setup to use in a TypeScript project. This section only contains some helpful hints to make the most out of the Effection related typings in your project.

TL;DR

Use the Operation type for your operations

import type { Operation } from "effection";

export function* op(): Operation<number> {
  yield* sleep(10);
  return 5;
}

If you see a weird type like Generator<Instruction, T> or Iterable<Instruction,T> or IterableIterator<Instruction, T>, replace it with Operation<T>.

Use Operations Everywhere

The foundation of Effection is the Operation<T> type. This is the type that it uses internally to represent all actions and resources, and it is the type that you should both consume and return from your own functions. For the most part, TypeScript will infer this for you, and you don't need to worry about it. However, there are some cases you may want to give it a hint that what you have is an operation, even though it will work without it. This is because Operation<T> is effectively an Iterable<Instruction, T>. This means that when you create this shape naturally in your code, then it will correctly slot into other operations, but it might be confusing when people look at types in their IDE. Take the following generator function:

function* op() {
  yield* sleep(10);
  return 5;
}

The natural inferred return type of this function is Generator<Instruction,T> which satisfies Iterable<Instruction, T> and so can be used as an operation, but it loses the higher-level intent of the operation. Instead, write op() like this:

function* op(): Operation<number> {
  yield* sleep(10);
  return 5;
}

By pinning the return type to Operation<number>, it communicates the intent of how the generator is to be used beyond just the literal shape of the generator itself.

The same applies for Operation values:

const op = {
  *[Symbol.iterator]() {
    yield* sleep(10);
    return 5;
  }
}

Strictly speaking the type of op is:

{
  [Symbol.iterator](): Generator<Instruction, T, any>
}

While this is technically correct, and it will work if you use it as an operation, it isn't particularly helpful to your end users. Instead, give them a leg up, and make their editor's tooltip show that they can evaluate it and expect to receive a number.

  const op: Operation<number> = {
  *[Symbol.iterator]() {
    yield* sleep(10);
    return 5;
  }
}

Now anybody using your operation won't have any doubt about what it is and how they can use it.

  • PreviousInstallation
  • NextThinking in Effection