Context.get

Building up a context is not much use unless we are able to retrieve things from it. The unsafeMap is, as the name suggests, not the preferred way to access services from a context.

Context.get(context, tag) returns the service implementation for the given tag from the given context.

import { Context } from "effect";

export class ActiveUser extends Context.Tag("@myapp/ActiveUser")<
	ActiveUser,
	{ name: string }
>() {}

const Greeting = Context.GenericTag<"GreetingId", string>("@myapp/Greeting");

const initial: Context.Context<"GreetingId"> = Greeting.context("Welcome");
const combined: Context.Context<"GreetingId" | ActiveUser> = initial.pipe(
	Context.add(ActiveUser, { name: "Jane" }),
);

const jane = Context.get(combined, ActiveUser);
const welcome = combined.pipe(Context.get(Greeting));
console.log(welcome, jane.name);

Getting a tag from context is type-safe. Note that in the previous example, jane.name is correctly recognised as a string (but welcome.name would not pass type-checking).

Note that just like with Effects, there is a dual API for Context, to make it easier to support piping operations. To call the function immediately, pass all arguments with the Context (“self”) as the first argument. To defer evaluation and return a function instead, pass all arguments except “self”.

Context.get(context, tag); // self-first
Context.get(tag)(context); // self-last

Context.add(context, tag, service); // self-first
Context.add(tag, service)(context); // self-last

Context is also type-safe in its keys. You cannot get a service that is not available from that Context.

Context.get(initial, ActiveUser);
// error TS2769: No overload matches this call.
//   Overload 1 of 4, '(self: Context<"GreetingId">, tag: Reference<unknown, unknown>): unknown', gave the following error.
//     Argument of type 'typeof ActiveUser' is not assignable to parameter of type 'Reference<unknown, unknown>'.
//       Type 'typeof ActiveUser' is missing the following properties from type 'Reference<unknown, unknown>': defaultValue, [ReferenceTypeId]
//   Overload 2 of 4, '(self: Context<"GreetingId">, tag: Tag<"GreetingId", any>): any', gave the following error.
//     Argument of type 'typeof ActiveUser' is not assignable to parameter of type 'Tag<"GreetingId", any>'.
//       Types of property 'Identifier' are incompatible.
//         Type 'ActiveUser' is not assignable to type '"GreetingId"'.

(Recall that adding an ActiveUser to initial will create a new Context, leaving initial untouched.)

Let’s recap:

  • Context.get(context, tag) can retrieve a service from a context
  • The type of the returned value will match the service type of the tag passed in
  • It is a compile-time error to get a tag that is not in the context
  • Operations on Context are plain (non-effectful) operations