Effect 3.15 (Release)
Effect 3.15 has been released! This release includes a number of new features and improvements. Here’s a summary of what’s new:
You can now convert a Stream
to an AsyncIterable
for integration with other libraries or APIs.
import { Stream } from "effect"
// Will print:// 1// 2// 3const stream = Stream.make(1, 2, 3)for await (const result of Stream.toAsyncIterable(stream)) { console.log(result)}
The catchTag
method now supports multiple tags.
This allows you to handle multiple error types in a single catch block.
import { Data, Effect } from "effect"
class ErrorA extends Data.TaggedError("ErrorA") {}class ErrorB extends Data.TaggedError("ErrorB") {}
declare const effect: Effect.Effect<never, ErrorA | ErrorB>
effect.pipe( Effect.catchTag("ErrorA", "ErrorB", (error: ErrorA | ErrorB) => { // Handle ErrorA and ErrorB with the same logic return Effect.void }))
These apis have been improved to support type narrowing in the fallback function.
import { Effect, Predicate } from "effect"
declare const effect: Effect.Effect<string | number>
effect.pipe( Effect.filterOrElse(Predicate.isString, (value: number) => { // The `value` type is now narrowed to `number` here return Effect.succeed(`Value is not a string: ${value}`) }))
This api allows you to find the first key-value pair in a record that satisfies a given predicate.
It returns an Option
containing the first matching key-value pair, or None
if no such pair is found.
import { Record, Option } from "effect"
const record = { a: 1, b: 2, c: 3 }const result = Record.findFirst( record, (value, key) => value > 1 && key !== "b")assert.deepStrictEqual(result, Option.some(["c", 3]))
This function allows you to access the unbranded value of a branded type.
import { Brand } from "effect"
type BrandedNumber = Brand<number, "Branded">declare const brandedValue: BrandedNumber
// Access the unbranded valueconst value: number = Brand.unbranded(brandedValue)
Applies an Either
on an Option
and transposes the result.
- If the
Option
isNone
, the resultingEither
will immediately succeed with aRight
value ofNone
. - If the
Option
isSome
, the transformation function will be applied to the inner value, and its result wrapped in aSome
.
import { Either, Option, pipe } from "effect"
// ┌─── Either<Option<number>, never>>// ▼const noneResult = pipe( Option.none(), Either.transposeMapOption(() => Either.right(42)) // will not be executed)console.log(noneResult)// Output: { _id: 'Either', _tag: 'Right', right: { _id: 'Option', _tag: 'None' } }
// ┌─── Either<Option<number>, never>>// ▼const someRightResult = pipe( Option.some(42), Either.transposeMapOption((value) => Either.right(value * 2)))console.log(someRightResult)// Output: { _id: 'Either', _tag: 'Right', right: { _id: 'Option', _tag: 'Some', value: 84 } }
You can now create a Layer that sets a custom random generator for your application.
import type { Random } from "effect"import { Effect, Layer } from "effect"
declare const myCustomRandom: Random.Random
Effect.void.pipe( Effect.provide( // You can now use `Layer.setRandom` to set a custom random generator Layer.setRandom(myCustomRandom) ))
Pipeable.Class
has been added, for adding a .pipe method to a classmessage
property has been added toConfigError
’sCause.isTimeoutException
is now availableFunction.apply
now accepts mulitple arguments
There were several other smaller changes made. Take a look through the CHANGELOG to see them all: CHANGELOG.
Don’t forget to join our Discord Community to follow the last updates and discuss every tiny detail!