> ## 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.

# Refactors

> Code refactors available in the Effect Language Service, with before/after examples for every transformation.

The Effect Language Service exposes refactor actions through your editor's refactor menu (typically **Ctrl+Shift+R** / **Cmd+Shift+R** or right-click → **Refactor…**). Each refactor inspects the AST at the cursor, validates the surrounding context, and rewrites the code in place.

<Note>
  All refactors work in both Effect V3 and V4 unless noted otherwise.
</Note>

## Async/await conversions

These refactors operate on any async function declaration, function expression, or arrow function. Place your cursor anywhere inside the function and trigger the refactor menu.

### `asyncAwaitToFn`

Converts an async/await function to `Effect.fn`, replacing `await expr` with `yield* Effect.promise(() => expr)` and wrapping the body in a generator. The function name is preserved as the trace string passed to `Effect.fn`.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
async function fetchUser(id: string) {
  const res = await fetch(`/users/${id}`)
  return res.json()
}

// After
const fetchUser = Effect.fn("fetchUser")(function* (id: string) {
  const res = yield* Effect.promise(() => fetch(`/users/${id}`))
  return yield* Effect.promise(() => res.json())
})
```

### `asyncAwaitToFnTryPromise`

Like `asyncAwaitToFn`, but wraps each awaited expression in `Effect.tryPromise` and generates a typed error ADT (`FetchError`, etc.) for each catch site, giving you a typed error channel instead of `UnknownException`.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
async function fetchUser(id: string) {
  const res = await fetch(`/users/${id}`)
  return res.json()
}

// After
class FetchUserError {
  readonly _tag = "FetchUserError"
  constructor(readonly cause: unknown) {}
}

const fetchUser = Effect.fn("fetchUser")(function* (id: string) {
  const res = yield* Effect.tryPromise({
    try: () => fetch(`/users/${id}`),
    catch: (cause) => new FetchUserError(cause)
  })
  return yield* Effect.tryPromise({
    try: () => res.json(),
    catch: (cause) => new FetchUserError(cause)
  })
})
```

### `asyncAwaitToGen`

Converts an async/await function to `Effect.gen`, replacing `await expr` with `yield* Effect.promise(() => expr)`. Unlike `asyncAwaitToFn`, the result is wrapped in `Effect.gen` rather than `Effect.fn`, so it is not a lazily-constructed pipeline.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
async function loadConfig() {
  const text = await readFile("config.json", "utf8")
  return JSON.parse(text)
}

// After
function loadConfig() {
  return Effect.gen(function* () {
    const text = yield* Effect.promise(() => readFile("config.json", "utf8"))
    return JSON.parse(text)
  })
}
```

### `asyncAwaitToGenTryPromise`

Like `asyncAwaitToGen`, but wraps each awaited expression in `Effect.tryPromise` with a generated typed error class.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
async function loadConfig() {
  const text = await readFile("config.json", "utf8")
  return JSON.parse(text)
}

// After
class LoadConfigError {
  readonly _tag = "LoadConfigError"
  constructor(readonly cause: unknown) {}
}

function loadConfig() {
  return Effect.gen(function* () {
    const text = yield* Effect.tryPromise({
      try: () => readFile("config.json", "utf8"),
      catch: (cause) => new LoadConfigError(cause)
    })
    return JSON.parse(text)
  })
}
```

***

## Generator refactors

### `effectGenToFn`

Converts an `Effect.gen(function*() { ... })` expression to `Effect.fn("name")(function*() { ... })`, promoting a one-shot generator into a reusable, traced pipeline function.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
const program = Effect.gen(function* () {
  const config = yield* Config
  return config.port
})

// After
const program = Effect.fn("program")(function* () {
  const config = yield* Config
  return config.port
})
```

### `wrapWithEffectGen`

Wraps the expression under the cursor in an `Effect.gen(function*() { return <expr> })` block, making it straightforward to add `yield*` statements around it.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
const value = computeResult()

// After
const value = Effect.gen(function* () {
  return computeResult()
})
```

### `removeUnnecessaryEffectGen`

Removes an `Effect.gen` wrapper when the generator body contains a single `return` statement with no `yield*` expressions, inlining the returned value directly.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
const value = Effect.gen(function* () {
  return Effect.succeed(42)
})

// After
const value = Effect.succeed(42)
```

***

## Function style

### `functionToArrow`

Converts a `function` declaration to an equivalent `const` arrow function, preserving modifiers (e.g., `export`), type parameters, and parameters.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
export function greet(name: string): string {
  return `Hello, ${name}`
}

// After
export const greet = (name: string): string => `Hello, ${name}`
```

### `toggleLazyConst`

Toggles a `const` variable between a lazy thunk `() => value` and an eager `value`. Useful for deferring expensive computations or removing unnecessary laziness.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before (eager)
const layer = makeLayer()

// After (lazy)
const layer = () => makeLayer()
```

### `togglePipeStyle`

Toggles between the `pipe(subject, f1, f2)` free-function style and the `subject.pipe(f1, f2)` method-call style. The refactor detects which form is at the cursor and offers the opposite.

**Support:** V3 ✓ · V4 ✓

<CodeGroup>
  ```typescript pipe → .pipe theme={null}
  // Before
  const result = pipe(
    Effect.succeed(1),
    Effect.map((n) => n + 1),
    Effect.map((n) => n * 2)
  )

  // After
  const result = Effect.succeed(1).pipe(
    Effect.map((n) => n + 1),
    Effect.map((n) => n * 2)
  )
  ```

  ```typescript .pipe → pipe theme={null}
  // Before
  const result = Effect.succeed(1).pipe(
    Effect.map((n) => n + 1)
  )

  // After
  const result = pipe(
    Effect.succeed(1),
    Effect.map((n) => n + 1)
  )
  ```
</CodeGroup>

### `toggleReturnTypeAnnotation`

Adds or removes an explicit return type annotation on a function. When adding, it infers the type from the type checker and inserts it.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before (no annotation)
const getPort = (config: Config) => config.port

// After (with annotation)
const getPort = (config: Config): number => config.port
```

### `toggleTypeAnnotation`

Adds or removes an explicit type annotation on a `const` variable declaration. When adding, it infers the type from the type checker.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before (no annotation)
const port = config.port

// After (with annotation)
const port: number = config.port
```

### `pipeableToDatafirst`

Converts pipeable-style calls (where the subject is passed as the last argument) to data-first style (where the subject is the first argument). Operates on the call expression at the cursor.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before (pipeable)
const result = Effect.map(effect, (n) => n + 1)

// After (data-first via pipe)
const result = effect.pipe(Effect.map((n) => n + 1))
```

### `wrapWithPipe`

Wraps the current selection in a `pipe(...)` call. Select any expression, then apply this refactor to make it the first argument of a new `pipe` invocation.

**Support:** V3 ✗ · V4 ✓

```typescript theme={null}
// Before (selection: Effect.succeed(42))
const result = Effect.succeed(42)

// After
const result = pipe(Effect.succeed(42))
```

### `missedPipeableOpportunity` (quick fix)

Triggered as a quick fix alongside the `missedPipeableOpportunity` diagnostic. Rewrites nested function calls into a single `pipe` call or `.pipe` chain, depending on your style preference.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before (nested calls triggering diagnostic)
const result = Effect.map(Effect.map(Effect.succeed(1), (n) => n + 1), (n) => n * 2)

// After
const result = Effect.succeed(1).pipe(
  Effect.map((n) => n + 1),
  Effect.map((n) => n * 2)
)
```

***

## Layer composition

### `layerMagic`

Automatically composes a set of layers into a correctly ordered pipeline of `Layer.provide`, `Layer.merge`, and `Layer.provideMerge` calls. This refactor has two phases:

<Steps>
  <Step title="Prepare">
    Place the cursor on a layer expression and apply **Prepare layers for automatic composition**. The refactor flattens all leaf layers into an array literal and wraps it with a cast: `([L1, L2, ...] as any) as Layer.Layer<TargetService>`. The union of output types is computed and annotated on the cast.
  </Step>

  <Step title="Build">
    With the cursor on the prepared cast expression, apply **Compose layers automatically with target output services**. The refactor reads the dependency graph, determines the correct composition order, and rewrites the expression to `L1.pipe(Layer.provideMerge(L2), Layer.provide(L3), ...)`. Any output types that could not be satisfied are listed in a trailing comment.
  </Step>
</Steps>

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Prepare step — mark layers for composition
const AppLayer = ([DatabaseLayer, CacheLayer, HttpLayer] as any) as Layer.Layer<App>

// Build step — auto-composed result
const AppLayer = DatabaseLayer.pipe(
  Layer.provideMerge(CacheLayer),
  Layer.provide(HttpLayer)
)
```

<Tip>
  Use the Layer Graph hover (see [Layer Graph](/features/layer-graph)) to visualise the dependency structure before running the build step.
</Tip>

***

## Schema generation

### `makeSchemaOpaque`

Converts a schema variable declaration into an opaque type pattern. Renames the original variable to `Name_` and inserts exported opaque interface/type aliases for `Name`, `NameEncoded`, and `NameContext` (V3) or `NameDecodingServices` / `NameEncodingServices` (V4), plus a re-exported `const Name` with a full type annotation.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
const UserId = Schema.String.pipe(Schema.brand("UserId"))

// After (V3 output)
const UserId_ = Schema.String.pipe(Schema.brand("UserId"))
export interface UserId extends Schema.Schema.Type<typeof UserId_> {}
export interface UserIdEncoded extends Schema.Schema.Encoded<typeof UserId_> {}
export type UserIdContext = Schema.Schema.Context<typeof UserId_>
export const UserId: Schema.Schema<UserId, UserIdEncoded, UserIdContext> = UserId_
```

### `makeSchemaOpaqueWithNs`

Like `makeSchemaOpaque`, but additionally wraps the generated types inside a namespace matching the variable name, giving you `UserId.Type`, `UserId.Encoded`, etc.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
const UserId = Schema.String.pipe(Schema.brand("UserId"))

// After
export namespace UserId {
  export interface Type extends Schema.Schema.Type<typeof UserId_> {}
  export interface Encoded extends Schema.Schema.Encoded<typeof UserId_> {}
  export type Context = Schema.Schema.Context<typeof UserId_>
}
const UserId_ = Schema.String.pipe(Schema.brand("UserId"))
export const UserId: Schema.Schema<UserId.Type, UserId.Encoded, UserId.Context> = UserId_
```

### `structuralTypeToSchema`

Generates a recursive `Effect.Schema` from the structural shape of a type alias or interface, following all referenced types transitively. Unlike `typeToEffectSchema`, this uses the resolved type information from the checker rather than the syntactic form of the declaration.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before — type alias with nested references
type Address = { street: string; city: string }
type User = { name: string; age: number; address: Address }

// After — inserted above the type alias
const Address = Schema.Struct({
  street: Schema.String,
  city: Schema.String
})

const User = Schema.Struct({
  name: Schema.String,
  age: Schema.Number,
  address: Address
})

type Address = { street: string; city: string }
type User = { name: string; age: number; address: Address }
```

### `typeToEffectSchema`

Generates an `Effect.Schema` declaration from a type alias or interface declaration. The cursor must be on the type name. Inserts the new schema constant immediately before the type declaration.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
type User = {
  name: string
  age: number
}

// After
const User = Schema.Struct({
  name: Schema.String,
  age: Schema.Number
})

type User = {
  name: string
  age: number
}
```

### `typeToEffectSchemaClass`

Generates a `Schema.Class` declaration from a type alias or interface. The class form gives you a nominal type with a class-based constructor, which is useful for tagged structs and error types.

**Support:** V3 ✓ · V4 ✓

```typescript theme={null}
// Before
type User = {
  name: string
  age: number
}

// After
class User extends Schema.Class<User>("User")({
  name: Schema.String,
  age: Schema.Number
}) {}

type User = {
  name: string
  age: number
}
```

***

## Service accessors

### `writeTagClassAccessors`

Generates `static override` accessor methods on an `Effect.Service` or `Effect.Tag` class for service methods that have generic type parameters or multiple overloads. Each generated accessor delegates to the service method via `Effect.andThen`, providing a convenient static entry point with the full type signature.

**Support:** V3 ✓ · V4 ✗ (not applicable in V4's `ServiceMap` model)

```typescript theme={null}
// Before
class Logger extends Effect.Service<Logger>()("Logger", {
  accessors: true,
  effect: Effect.gen(function* () {
    return {
      log: <A>(value: A): Effect.Effect<void> => Console.log(value)
    }
  })
}) {}

// After — accessor inserted inside the class body
class Logger extends Effect.Service<Logger>()("Logger", {
  accessors: true,
  effect: Effect.gen(function* () {
    return {
      log: <A>(value: A): Effect.Effect<void> => Console.log(value)
    }
  })
}) {
  static override log: <A>(value: A) => Effect.Effect<void, never, Logger> =
    (...args) => Effect.andThen(Logger, (_) => _.log(...args))
}
```
