Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dry-cycles-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Add type-level utils to asserting layer types
28 changes: 27 additions & 1 deletion packages/effect/dtslint/Layer.tst.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Layer, Schedule } from "effect"
import { Context, Layer, Schedule } from "effect"
import { describe, expect, it } from "tstyche"

interface In1 {}
Expand All @@ -19,6 +19,8 @@ interface Out3 {}

declare const layer3: Layer.Layer<Out3, Err3, In3>

class TestService1 extends Context.Tag("TestService1")<TestService1, {}>() {}

describe("Layer", () => {
it("merge", () => {
expect(Layer.merge).type.not.toBeCallableWith()
Expand All @@ -40,4 +42,28 @@ describe("Layer", () => {
expect(Layer.retry(layer1, Schedule.recurs(1))).type.toBe<Layer.Layer<Out1, Err1, In1>>()
expect(layer1.pipe(Layer.retry(Schedule.recurs(1)))).type.toBe<Layer.Layer<Out1, Err1, In1>>()
})

it("ensureSuccessType", () => {
expect(layer1.pipe(Layer.ensureSuccessType<Out1>())).type.toBe<Layer.Layer<Out1, Err1, In1>>()
})

it("ensureErrorType", () => {
const withoutError = Layer.succeed(TestService1, {})
expect(withoutError.pipe(Layer.ensureErrorType<never>())).type.toBe<Layer.Layer<TestService1, never, never>>()

const withError = layer1
expect(withError.pipe(Layer.ensureErrorType<Err1>())).type.toBe<Layer.Layer<Out1, Err1, In1>>()
})

it("ensureRequirementsType", () => {
const withoutRequirements = Layer.succeed(TestService1, {})
expect(withoutRequirements.pipe(Layer.ensureRequirementsType<never>())).type.toBe<
Layer.Layer<TestService1, never, never>
>()

const withRequirement = layer1
expect(withRequirement.pipe(Layer.ensureRequirementsType<In1>())).type.toBe<
Layer.Layer<Out1, Err1, In1>
>()
})
})
52 changes: 52 additions & 0 deletions packages/effect/src/Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1226,3 +1226,55 @@ export const updateService = dual<
layer,
map(context(), (c) => Context.add(c, tag, f(Context.unsafeGet(c, tag))))
))

// -----------------------------------------------------------------------------
// Type constraints
// -----------------------------------------------------------------------------

/**
* A no-op type constraint that enforces the success channel of a Layer conforms to
* the specified success type `ROut`.
*
* @example
* import { Layer } from "effect"
*
* // Ensure that the layer produces the expected services.
* const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureSuccessType<MyService>())
*
* @since 3.20.0
* @category Type constraints
*/
export const ensureSuccessType =
<ROut>() => <ROut2 extends ROut, E, RIn>(layer: Layer<ROut2, E, RIn>): Layer<ROut2, E, RIn> => layer

/**
* A no-op type constraint that enforces the error channel of a Layer conforms to
* the specified error type `E`.
*
* @example
* import { Layer } from "effect"
*
* // Ensure that the layer does not expose any unhandled errors.
* const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureErrorType<never>())
*
* @since 3.20.0
* @category Type constraints
*/
export const ensureErrorType = <E>() => <ROut, E2 extends E, RIn>(layer: Layer<ROut, E2, RIn>): Layer<ROut, E2, RIn> =>
layer

/**
* A no-op type constraint that enforces the requirements channel of a Layer conforms to
* the specified requirements type `RIn`.
*
* @example
* import { Layer } from "effect"
*
* // Ensure that the layer does not have any requirements.
* const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureRequirementsType<never>())
*
* @since 3.20.0
* @category Type constraints
*/
export const ensureRequirementsType =
<RIn>() => <ROut, E, RIn2 extends RIn>(layer: Layer<ROut, E, RIn2>): Layer<ROut, E, RIn2> => layer