Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Effect-TS/tsgo/llms.txt

Use this file to discover all available pages before exploring further.

Style diagnostics suggest code improvements that make Effect code more idiomatic, concise, and consistent. Most include a quick fix (🔧) so the improvement is one keystroke away. All are suggestions or off by default — they never block compilation.
Rules marked 💡 are suggestions. Rules marked ➖ are off by default and must be enabled in diagnosticSeverity.
V3/V4: ✓ / ✓Suggests using Effect.mapError instead of the pattern Effect.catchAll(e => Effect.fail(transform(e))). When you only need to transform the error type without recovering, mapError is the idiomatic choice.
import { Effect } from "effect"

// ❌ Before — verbose catch + fail pattern
const result = someEffect.pipe(
  Effect.catchAll((e) => Effect.fail(new WrappedError({ cause: e })))
)

// ✅ After (quick fix applied) — concise mapError
const result = someEffect.pipe(
  Effect.mapError((e) => new WrappedError({ cause: e }))
)
V3/V4: ✓ / ✓Enforces deterministic, file-path-based naming for service identifiers, tag identifiers, and error tag strings. Consistent key naming prevents accidental collisions when the same identifier is used in different files. Configure the naming formula with keyPatterns in the plugin options.
import { Effect } from "effect"

// ❌ Violation (when enabled) — key does not follow the configured pattern
class Database extends Effect.Service<Database>()("DB", {
  effect: Effect.succeed({ query: () => Effect.void })
}) {}

// ✅ After quick fix — key matches the deterministic formula
class Database extends Effect.Service<Database>()("services/Database", {
  effect: Effect.succeed({ query: () => Effect.void })
}) {}
To enable:
{ "diagnosticSeverity": { "deterministicKeys": "warning" } }
V3/V4: ✓ / ✓Suggests wrapping functions that return an Effect with Effect.fn to get automatic tracing, span naming, and a cleaner call signature. Applies to function declarations and arrow functions at module scope.
import { Effect } from "effect"

// ❌ Before — plain function returning Effect
const getUser = (id: string) =>
  Effect.gen(function* () {
    const db = yield* Database
    return yield* db.findById(id)
  })

// ✅ After (quick fix applied) — wrapped with Effect.fn for tracing
const getUser = Effect.fn("getUser")((id: string) =>
  Effect.gen(function* () {
    const db = yield* Database
    return yield* db.findById(id)
  })
)
V3/V4: ✓ / ✓Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {}). Effect.asVoid is the idiomatic way to discard a success value.
import { Effect } from "effect"

// ❌ Before — verbose map to void
const result = someEffect.pipe(
  Effect.map(() => void 0)
)

// ✅ After (quick fix applied)
const result = someEffect.pipe(
  Effect.asVoid
)
V3/V4: ✓ / ✓Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0). Effect.void is the canonical empty-success value.
import { Effect } from "effect"

// ❌ Before
const noop = Effect.succeed(undefined)

// ✅ After (quick fix applied)
const noop = Effect.void
V3/V4: ✓ / ✓Suggests converting deeply nested function calls to .pipe() style for better readability. Triggers when at least pipeableMinArgCount (default: 2) contiguous pipeable transformations are nested.
import { Effect } from "effect"

// ❌ Before — nested calls, hard to read
const result = Effect.mapError(
  Effect.map(someEffect, (x) => x + 1),
  (e) => new WrappedError({ cause: e })
)

// ✅ After (quick fix applied) — pipe style
const result = someEffect.pipe(
  Effect.map((x) => x + 1),
  Effect.mapError((e) => new WrappedError({ cause: e }))
)
To enable:
{ "diagnosticSeverity": { "missedPipeableOpportunity": "suggestion" } }
V3 only  ·  V4:Checks that an Effect.Service class’s declared dependencies array satisfies all required layer inputs. If a service’s implementation requires another service but that service is not listed in dependencies, the layer will be incomplete.
import { Effect } from "effect"

class Config extends Effect.Service<Config>()("Config", {
  effect: Effect.succeed({ url: "http://localhost" })
}) {}

// ❌ Violation (when enabled) — Config is required but not in dependencies
class HttpClient extends Effect.Service<HttpClient>()("HttpClient", {
  effect: Effect.gen(function* () {
    const config = yield* Config
    return { get: () => Effect.void }
  }),
  // dependencies: [Config.Default] // missing!
}) {}

// ✅ Correct — Config.Default listed in dependencies
class HttpClient extends Effect.Service<HttpClient>()("HttpClient", {
  effect: Effect.gen(function* () {
    const config = yield* Config
    return { get: () => Effect.void }
  }),
  dependencies: [Config.Default]
}) {}
To enable:
{ "diagnosticSeverity": { "missingEffectServiceDependency": "warning" } }
V3/V4: ✓ / ✓Suggests removing the redundant identifier argument from Schema.TaggedClass, Schema.TaggedError, and Schema.TaggedRequest when the identifier is the same as the class name. The class name is used by default, so passing it explicitly is noise.
import { Schema } from "effect"

// ❌ Before — identifier duplicates the class name
class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", {
  id: Schema.String
}) {}

// ✅ After (quick fix applied) — identifier removed
class UserNotFound extends Schema.TaggedError<UserNotFound>()({
  id: Schema.String
}) {}
V3/V4: ✓ / ✓Suggests replacing Schema.Struct with a _tag field with Schema.TaggedStruct. Schema.TaggedStruct makes the _tag field optional in the constructor (it defaults to the tag value), reducing boilerplate.
import { Schema } from "effect"

// ❌ Before — Schema.Struct with explicit _tag
const UserEvent = Schema.Struct({
  _tag: Schema.Literal("UserEvent"),
  userId: Schema.String,
})

// ✅ After (quick fix applied) — Schema.TaggedStruct
const UserEvent = Schema.TaggedStruct("UserEvent", {
  userId: Schema.String,
})
V3 only  ·  V4:Suggests combining multiple Schema.Literal calls inside a Schema.Union into a single Schema.Literal call. A single Schema.Literal with multiple arguments is equivalent and more concise.
import { Schema } from "effect"

// ❌ Before — multiple Literal in Union
const Status = Schema.Union(
  Schema.Literal("active"),
  Schema.Literal("inactive"),
  Schema.Literal("pending")
)

// ✅ After (quick fix applied) — single Literal
const Status = Schema.Literal("active", "inactive", "pending")
To enable:
{ "diagnosticSeverity": { "schemaUnionOfLiterals": "suggestion" } }
V4 only  ·  V3:Warns when ServiceMap.Service is used as a variable expression instead of a class declaration. The class declaration form is the idiomatic V4 pattern and enables better type inference, tooling support, and accessor generation.
import { ServiceMap } from "effect"

// ❌ Violation (when enabled) — variable assignment form
const Database = ServiceMap.Service<Database>()("Database", {
  effect: Effect.succeed({ query: () => Effect.void })
})

// ✅ After quick fix — class declaration form
class Database extends ServiceMap.Service<Database>()("Database", {
  effect: Effect.succeed({ query: () => Effect.void })
}) {}
To enable:
{ "diagnosticSeverity": { "serviceNotAsClass": "warning" } }
V3/V4: ✓ / ✓Enforces that only strictly boolean values are used in conditional expressions. Disallows truthy/falsy coercion of strings, numbers, null, and undefined in conditions. Mirrors TypeScript’s strictNullChecks spirit for conditionals.
// ❌ Violation (when enabled) — number used in condition (truthy coercion)
const count = getCount()
if (count) { // count is number, not boolean
  processItems()
}

// ✅ Correct — explicit boolean comparison
const count = getCount()
if (count > 0) {
  processItems()
}
To enable:
{ "diagnosticSeverity": { "strictBooleanExpressions": "warning" } }
V3/V4: ✓ / ✓Suggests removing the Effect.gen wrapper when the generator body contains only a single return statement. A single-expression generator adds indentation and function* syntax with no benefit.
import { Effect } from "effect"

// ❌ Before — unnecessary Effect.gen for a single expression
const result = Effect.gen(function* () {
  return yield* Effect.succeed(42)
})

// ✅ After (quick fix applied) — direct expression
const result = Effect.succeed(42)
V3/V4: ✓ / ✓Suggests yielding yieldable error types (such as Data.TaggedError instances) directly with yield* instead of wrapping them in Effect.fail. Tagged errors implement the Yieldable interface, so they can be yielded directly for a more concise syntax.
import { Effect, Data } from "effect"

class NotFoundError extends Data.TaggedError("NotFoundError")<{}> {}

// ❌ Before — wrapping a yieldable error in Effect.fail
const program = Effect.gen(function* () {
  return yield* Effect.fail(new NotFoundError())
})

// ✅ After (quick fix applied) — yield* the error directly
const program = Effect.gen(function* () {
  return yield* new NotFoundError()
})
V3/V4: ✓ / ✓Removes pipe() calls that contain no arguments. An empty pipe() call is a no-op and should be removed.
import { pipe } from "effect"

// ❌ Before — pipe with no arguments
const result = pipe(someValue)

// ✅ After (quick fix applied)
const result = someValue
V3/V4: ✓ / ✓Simplifies chained pipe calls into a single pipe call. When multiple pipe(...) calls are chained, they can always be flattened into one, improving readability.
import { pipe, Effect } from "effect"

// ❌ Before — chained pipe calls
const result = pipe(
  pipe(
    someEffect,
    Effect.map((x) => x + 1)
  ),
  Effect.mapError((e) => new WrappedError({ cause: e }))
)

// ✅ After (quick fix applied) — single pipe call
const result = pipe(
  someEffect,
  Effect.map((x) => x + 1),
  Effect.mapError((e) => new WrappedError({ cause: e }))
)

Build docs developers (and LLMs) love