Skip to main content Link Search Menu Expand Document (external link)

Context.ts overview

This module provides a data structure called Context that can be used for dependency injection in effectful programs. It is essentially a table mapping Tags to their implementations (called Services), and can be used to manage dependencies in a type-safe way. The Context data structure is essentially a way of providing access to a set of related services that can be passed around as a single unit. This module provides functions to create, modify, and query the contents of a Context, as well as a number of utility types for working with tags and services.

Since v2.0.0


Exports Grouped by Category


constructors

GenericTag

Creates a new Tag instance with an optional key parameter.

Example

import * as assert from "node:assert"
import { Context } from "effect"

assert.strictEqual(Context.GenericTag("PORT").key === Context.GenericTag("PORT").key, true)

Signature

declare const GenericTag: <Identifier, Service = Identifier>(key: string) => Tag<Identifier, Service>

Source

Since v2.0.0

Reference

Creates a context tag with a default value.

Details

Context.Reference allows you to create a tag that can hold a value. You can provide a default value for the service, which will automatically be used when the context is accessed, or override it with a custom implementation when needed.

Example (Declaring a Tag with a default value)

import * as assert from "node:assert"
import { Context, Effect } from "effect"

class SpecialNumber extends Context.Reference<SpecialNumber>()("SpecialNumber", { defaultValue: () => 2048 }) {}

//      ┌─── Effect<void, never, never>
//      â–¼
const program = Effect.gen(function* () {
  const specialNumber = yield* SpecialNumber
  console.log(`The special number is ${specialNumber}`)
})

// No need to provide the SpecialNumber implementation
Effect.runPromise(program)
// Output: The special number is 2048

Example (Overriding the default value)

import { Context, Effect } from "effect"

class SpecialNumber extends Context.Reference<SpecialNumber>()("SpecialNumber", { defaultValue: () => 2048 }) {}

const program = Effect.gen(function* () {
  const specialNumber = yield* SpecialNumber
  console.log(`The special number is ${specialNumber}`)
})

Effect.runPromise(program.pipe(Effect.provideService(SpecialNumber, -1)))
// Output: The special number is -1

Signature

declare const Reference: <Self>() => <const Id extends string, Service>(
  id: Id,
  options: { readonly defaultValue: () => Service }
) => ReferenceClass<Self, Id, Service>

Source

Since v3.11.0

Tag

Example

import * as assert from "node:assert"
import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")<MyTag, { readonly myNum: number }>() {
  static Live = Layer.succeed(this, { myNum: 108 })
}

Signature

declare const Tag: <const Id extends string>(id: Id) => <Self, Shape>() => TagClass<Self, Id, Shape>

Source

Since v2.0.0

empty

Returns an empty Context.

Example

import * as assert from "node:assert"
import { Context } from "effect"

assert.strictEqual(Context.isContext(Context.empty()), true)

Signature

declare const empty: () => Context<never>

Source

Since v2.0.0

make

Creates a new Context with a single service associated to the tag.

Example

import * as assert from "node:assert"
import { Context } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")

const Services = Context.make(Port, { PORT: 8080 })

assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })

Signature

declare const make: <I, S>(tag: Tag<I, S>, service: Types.NoInfer<S>) => Context<I>

Source

Since v2.0.0

unsafeMake

Signature

declare const unsafeMake: <Services>(unsafeMap: Map<string, any>) => Context<Services>

Source

Since v2.0.0

getters

get

Get a service from the context that corresponds to the given tag.

Example

import * as assert from "node:assert"
import { pipe, Context } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const Services = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))

assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })

Signature

declare const get: {
  <I, S>(tag: Reference<I, S>): <Services>(self: Context<Services>) => S
  <Services, I extends Services, S>(tag: Tag<I, S>): (self: Context<Services>) => S
  <Services, I, S>(self: Context<Services>, tag: Reference<I, S>): S
  <Services, I extends Services, S>(self: Context<Services>, tag: Tag<I, S>): S
}

Source

Since v2.0.0

getOption

Get the value associated with the specified tag from the context wrapped in an Option object. If the tag is not found, the Option object will be None.

Example

import * as assert from "node:assert"
import { Context, Option } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const Services = Context.make(Port, { PORT: 8080 })

assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none())

Signature

declare const getOption: {
  <S, I>(tag: Tag<I, S>): <Services>(self: Context<Services>) => Option<S>
  <Services, S, I>(self: Context<Services>, tag: Tag<I, S>): Option<S>
}

Source

Since v2.0.0

getOrElse

Get a service from the context that corresponds to the given tag, or use the fallback value.

Signature

declare const getOrElse: {
  <S, I, B>(tag: Tag<I, S>, orElse: LazyArg<B>): <Services>(self: Context<Services>) => S | B
  <Services, S, I, B>(self: Context<Services>, tag: Tag<I, S>, orElse: LazyArg<B>): S | B
}

Source

Since v3.7.0

guards

isContext

Checks if the provided argument is a Context.

Example

import * as assert from "node:assert"
import { Context } from "effect"

assert.strictEqual(Context.isContext(Context.empty()), true)

Signature

declare const isContext: (input: unknown) => input is Context<never>

Source

Since v2.0.0

isReference

Checks if the provided argument is a Reference.

Signature

declare const isReference: (u: unknown) => u is Reference<any, any>

Source

Since v3.11.0

isTag

Checks if the provided argument is a Tag.

Example

import * as assert from "node:assert"
import { Context } from "effect"

assert.strictEqual(Context.isTag(Context.GenericTag("Tag")), true)

Signature

declare const isTag: (input: unknown) => input is Tag<any, any>

Source

Since v2.0.0

models

Context (interface)

Signature

export interface Context<in Services> extends Equal, Pipeable, Inspectable {
  readonly [TypeId]: {
    readonly _Services: Types.Contravariant<Services>
  }
  readonly unsafeMap: Map<string, any>
}

Source

Since v2.0.0

Reference (interface)

Signature

export interface Reference<in out Id, in out Value> extends Pipeable, Inspectable {
  readonly [ReferenceTypeId]: ReferenceTypeId
  readonly defaultValue: () => Value

  readonly _op: "Tag"
  readonly Service: Value
  readonly Identifier: Id
  readonly [TagTypeId]: {
    readonly _Service: Types.Invariant<Value>
    readonly _Identifier: Types.Invariant<Id>
  }
  of(self: Value): Value
  context(self: Value): Context<Id>
  readonly stack?: string | undefined
  readonly key: string
  [Unify.typeSymbol]?: unknown
  [Unify.unifySymbol]?: TagUnify<this>
  [Unify.ignoreSymbol]?: TagUnifyIgnore
}

Source

Since v3.11.0

ReferenceClass (interface)

Signature

export interface ReferenceClass<Self, Id extends string, Type> extends Reference<Self, Type> {
  new (_: never): TagClassShape<Id, Type>
  readonly key: Id
}

Source

Since v3.11.0

Tag (interface)

Signature

export interface Tag<in out Id, in out Value> extends Pipeable, Inspectable {
  readonly _op: "Tag"
  readonly Service: Value
  readonly Identifier: Id
  readonly [TagTypeId]: {
    readonly _Service: Types.Invariant<Value>
    readonly _Identifier: Types.Invariant<Id>
  }
  of(self: Value): Value
  context(self: Value): Context<Id>
  readonly stack?: string | undefined
  readonly key: string
  [Unify.typeSymbol]?: unknown
  [Unify.unifySymbol]?: TagUnify<this>
  [Unify.ignoreSymbol]?: TagUnifyIgnore
}

Source

Since v3.5.9

TagClass (interface)

Signature

export interface TagClass<Self, Id extends string, Type> extends Tag<Self, Type> {
  new (_: never): TagClassShape<Id, Type>
  readonly key: Id
}

Source

Since v2.0.0

TagClassShape (interface)

Signature

export interface TagClassShape<Id, Shape> {
  readonly [TagTypeId]: TagTypeId
  readonly Type: Shape
  readonly Id: Id
}

Source

Since v2.0.0

TagUnify (interface)

Signature

export interface TagUnify<A extends { [Unify.typeSymbol]?: any }> {
  Tag?: () => Extract<A[Unify.typeSymbol], Tag<any, any>>
}

Source

Since v2.0.0

TagUnifyIgnore (interface)

Signature

export interface TagUnifyIgnore {}

Source

Since v2.0.0

ValidTagsById (type alias)

Signature

type ValidTagsById<R> = R extends infer S ? Tag<S, any> : never

Source

Since v2.0.0

symbol

ReferenceTypeId (type alias)

Signature

type ReferenceTypeId = typeof ReferenceTypeId

Source

Since v3.11.0

TagTypeId (type alias)

Signature

type TagTypeId = typeof TagTypeId

Source

Since v2.0.0

TypeId (type alias)

Signature

type TypeId = typeof TypeId

Source

Since v2.0.0

unsafe

unsafeGet

Get a service from the context that corresponds to the given tag. This function is unsafe because if the tag is not present in the context, a runtime error will be thrown.

For a safer version see getOption.

Example

import * as assert from "node:assert"
import { Context } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const Services = Context.make(Port, { PORT: 8080 })

assert.deepStrictEqual(Context.unsafeGet(Services, Port), { PORT: 8080 })
assert.throws(() => Context.unsafeGet(Services, Timeout))

Signature

declare const unsafeGet: {
  <S, I>(tag: Tag<I, S>): <Services>(self: Context<Services>) => S
  <Services, S, I>(self: Context<Services>, tag: Tag<I, S>): S
}

Source

Since v2.0.0

utils

Tag (namespace)

Source

Since v2.0.0

Service (type alias)

Signature

type Service<T> = T extends Tag<any, any> ? T["Service"] : T extends TagClassShape<any, infer A> ? A : never

Source

Since v2.0.0

Identifier (type alias)

Signature

type Identifier<T> = T extends Tag<any, any> ? T["Identifier"] : T extends TagClassShape<any, any> ? T : never

Source

Since v2.0.0

add

Adds a service to a given Context.

Example

import * as assert from "node:assert"
import { Context, pipe } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const someContext = Context.make(Port, { PORT: 8080 })

const Services = pipe(someContext, Context.add(Timeout, { TIMEOUT: 5000 }))

assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })

Signature

declare const add: {
  <I, S>(tag: Tag<I, S>, service: Types.NoInfer<S>): <Services>(self: Context<Services>) => Context<Services | I>
  <Services, I, S>(self: Context<Services>, tag: Tag<I, S>, service: Types.NoInfer<S>): Context<Services | I>
}

Source

Since v2.0.0

merge

Merges two Contexts, returning a new Context containing the services of both.

Example

import * as assert from "node:assert"
import { Context } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })

const Services = Context.merge(firstContext, secondContext)

assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })

Signature

declare const merge: {
  <R1>(that: Context<R1>): <Services>(self: Context<Services>) => Context<R1 | Services>
  <Services, R1>(self: Context<Services>, that: Context<R1>): Context<Services | R1>
}

Source

Since v2.0.0

mergeAll

Merges any number of Contexts, returning a new Context containing the services of all.

Example

import * as assert from "node:assert"
import { Context } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const Host = Context.GenericTag<{ HOST: string }>("Host")

const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
const thirdContext = Context.make(Host, { HOST: "localhost" })

const Services = Context.mergeAll(firstContext, secondContext, thirdContext)

assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })
assert.deepStrictEqual(Context.get(Services, Host), { HOST: "localhost" })

Signature

declare const mergeAll: <T extends Array<unknown>>(...ctxs: { [K in keyof T]: Context<T[K]> }) => Context<T[number]>

Source

Since v3.12.0

omit

Signature

declare const omit: <Tags extends ReadonlyArray<Tag<any, any>>>(
  ...tags: Tags
) => <Services>(self: Context<Services>) => Context<Exclude<Services, Tag.Identifier<Tags[number]>>>

Source

Since v2.0.0

pick

Returns a new Context that contains only the specified services.

Example

import * as assert from "node:assert"
import { pipe, Context, Option } from "effect"

const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")

const someContext = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))

const Services = pipe(someContext, Context.pick(Port))

assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none())

Signature

declare const pick: <Tags extends ReadonlyArray<Tag<any, any>>>(
  ...tags: Tags
) => <Services>(self: Context<Services>) => Context<Services & Tag.Identifier<Tags[number]>>

Source

Since v2.0.0