Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

369 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Effect Language Service (TypeScript-Go)

A wrapper around TypeScript-Go that builds the Effect Language Service, providing Effect-TS diagnostics and quick fixes. This project targets Effect V4 (codename: "smol") primarily and also Effect V3.

Installation

The setup of the TSGO version of the LSP can be performed via the command line interface:

npx @effect/tsgo setup

This will guide you through the installation process, which includes:

  1. Adding the @effect/tsgo dependency to your project.
  2. Configuring your tsconfig.json to use the Effect Language Service plugin.
  3. Adjusting plugin options to your preference.
  4. Hinting at any additional editor configuration needed to ensure the LSP is active.

Note

At the moment, you still need a native TypeScript install alongside @effect/tsgo: typescript >= 7 (e.g. typescript@latest or typescript@next) or an alias such as @typescript/native. effect-tsgo patch tries typescript, then @typescript/native, and accepts --typescript-package <name> to try a custom package name first.

LSP-based linter

The Effect LSP doubles as a tool to perform type-aware linting of Effect code, and ships as well a way to emit additional Oxlint Type Aware rules.

Linting can occur either during the tsc typecheck phase (with the benefit of running typechecking only once and caching the output), or via a dedicated npx @effect/tsgo diagnostics --project tsconfig.json command (with typechecking occurring again), or via the Oxlint Patch.

See the Oxlint Setup guide for instructions on how to install and configure Oxlint with the Effect LSP.

When running in tsc mode, the Effect diagnostics are emitted as standard TypeScript diagnostics, and can be configured to affect the tsc exit code through the options ignoreEffectSuggestionsInTscExitCode, ignoreEffectWarningsInTscExitCode, and ignoreEffectErrorsInTscExitCode.

When running in dedicated diagnostics mode, the Effect diagnostics can be emitted in structured formats, which can be further processed by other tools.

Diagnostic Status

Some diagnostics are off by default or have a default severity of suggestion, but you can always enable them or change their default severity in the plugin options.

RuleDescription
Correctness Wrong, unsafe, or structurally invalid code patterns.
anyUnknownInErrorContextDetects 'any' or 'unknown' types in Effect error or requirements channels
classSelfMismatchEnsures Self type parameter matches the class name in Context/Service/Tag/Schema classes
duplicatePackageWarns when multiple versions of an Effect-related package are detected in the program
effectFnImplicitAnyMirrors noImplicitAny for unannotated Effect.fn, Effect.fnUntraced, and Effect.fnUntracedEager callback parameters when no outer contextual function type exists. Requires TS's noImplicitAny: true
floatingEffectDetects Effect values that are neither yielded nor assigned
floatingEffectInVitestDetects Effects returned from non-Effect-aware Vitest callbacks
genericEffectServicesPrevents services with type parameters that cannot be discriminated at runtime
missingEffectContextDetects Effect values with unhandled context requirements
missingEffectErrorDetects Effect values with unhandled error types
missingLayerContextDetects Layer values with unhandled context requirements
missingReturnYieldStarSuggests using return yield* for Effects that never succeed
missingStarInYieldEffectGenDetects bare yield (without *) inside Effect generator scopes
nonObjectEffectServiceTypeEnsures Effect.Service types are objects, not primitives
outdatedApiDetects usage of APIs that have been removed or renamed in Effect v4
overriddenSchemaConstructorPrevents overriding constructors in Schema classes which breaks decoding behavior
promiseInEffectSuccessDetects Promise types in Effect success channels where they are not awaited
schemaLiteralNonFiniteReports statically known non-finite numbers passed to Schema literal constructors
schemaOpaqueInstanceMemberDisallows instance members in classes extending Schema.Opaque
Anti-pattern Discouraged patterns that often lead to bugs or confusing behavior.
catchUnfailableEffectWarns when using error handling on Effects that never fail
effectFnIifeEffect.fn or Effect.fnUntraced is called as an IIFE; use Effect.gen instead
effectGenUsesAdapterWarns when using the deprecated adapter parameter in Effect.gen
effectInFailureWarns when an Effect is used inside an Effect failure channel
effectInVoidSuccessDetects nested Effects in void success channels that may cause unexecuted effects
globalErrorInEffectCatchWarns when catch callbacks return global Error type instead of typed errors
globalErrorInEffectFailureWarns when the global Error type is used in an Effect failure channel
layerMergeAllWithDependenciesDetects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires
lazyEffectSuggests avoiding exported zero-argument functions and service members that lazily return Effect or Stream values
lazyPromiseInEffectSyncWarns when Effect.sync lazily returns a Promise instead of using an async Effect constructor
leakingRequirementsDetects implementation services leaked in service methods
multipleEffectProvideWarns against chaining Effect.provide calls which can cause service lifecycle issues
preferUnsafeConstructorSuggests replacing Effect.runSync of a pure effect constructor with the synchronous *Unsafe variant exported by the same module
returnEffectInGenWarns when returning an Effect in a generator causes nested Effect<Effect<...>>
runEffectInsideEffectSuggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts
schemaSyncInEffectSuggests using Effect-based Schema methods instead of sync methods inside Effect generators
scopeInLayerEffectSuggests using Layer.scoped instead of Layer.effect when Scope is in requirements
strictEffectProvideWarns when using Effect.provide with layers outside of application entry points
tryCatchInEffectGenDiscourages try/catch in Effect generators in favor of Effect error handling
unknownInEffectCatchWarns when catch callbacks return unknown instead of typed errors
Effect-native Prefer Effect-native APIs and abstractions when available.
abortControllerInEffectWarns when manually constructing AbortController inside Effect generators instead of using Effect.abortSignal
asyncFunctionWarns when declaring async functions and suggests using Effect values and Effect.gen for async control flow
cryptoRandomUUIDWarns when using crypto.randomUUID() outside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes
cryptoRandomUUIDInEffectWarns when using crypto.randomUUID() inside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes
extendsNativeErrorWarns when a class directly extends the native Error class
globalConsoleWarns when using console methods outside Effect generators instead of Effect.log/Logger
globalConsoleInEffectWarns when using console methods inside Effect generators instead of Effect.log/Logger
globalDateWarns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime
globalDateInEffectWarns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime
globalFetchWarns when using the global fetch function outside Effect generators instead of the Effect HTTP client
globalFetchInEffectWarns when using the global fetch function inside Effect generators instead of the Effect HTTP client
globalRandomWarns when using Math.random() outside Effect generators instead of the Random service
globalRandomInEffectWarns when using Math.random() inside Effect generators instead of the Random service
globalTimersWarns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule
globalTimersInEffectWarns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule
instanceOfSchemaSuggests using Schema.is instead of instanceof for Effect Schema types
newPromiseWarns when constructing promises with new Promise instead of using Effect APIs
nodeBuiltinImportWarns when importing Node.js built-in modules that have Effect-native counterparts
preferSchemaOverJsonSuggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify
processEnvWarns when reading process.env outside Effect generators instead of using Effect Config
processEnvInEffectWarns when reading process.env inside Effect generators instead of using Effect Config
unsafeEffectTypeAssertionDetects unsafe type assertions that narrow Effect, Stream, or Layer error or requirements channels
Style Cleanup, consistency, and idiomatic Effect code.
catchAllToMapErrorSuggests using Effect.mapError instead of Effect.catch + Effect.fail
catchChainToFirstSuccessOfSuggests Effect.firstSuccessOf for consecutive error-independent Effect.catch fallbacks when the error type is preserved
catchTagToCatchReasonSuggests Effect.catchReason or Effect.catchReasons for handlers that re-fail unmatched reason._tag branches
catchToIgnoreSuggests using Effect.ignore or Effect.ignoreCause instead of Effect.catch/catchCause returning Effect.void
catchToOrElseSucceedSuggests using Effect.orElseSucceed instead of Effect.catch + Effect.succeed
deterministicKeysEnforces deterministic naming for service/tag/error identifiers based on class names
effectDoNotationSuggests using Effect.gen or Effect.fn instead of the Effect.Do notation helpers
effectFnOpportunitySuggests using Effect.fn for functions that return an Effect
effectMapFlattenSuggests using Effect.flatMap instead of Effect.map followed by Effect.flatten in piping flows
effectMapVoidSuggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})
effectSucceedWithVoidSuggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)
flatMapToMapSuggests using Effect.map instead of Effect.flatMap when the callback only wraps its result with Effect.succeed
missedPipeableOpportunitySuggests using .pipe() for nested function calls
missingEffectServiceDependencyChecks that Effect.Service dependencies satisfy all required layer inputs
missingPipeableSignatureReports exported fixed-arity functions whose call signatures have no corresponding pipeable overload
multipleCatchTagSuggests collapsing consecutive Effect.catchTag transformations into a single Effect.catchTags call when semantics stay equivalent
nestedEffectGenYieldWarns when yielding a nested bare Effect.gen inside an existing Effect generator context
newSchemaClassSuggests using Schema make instead of new for Schema classes
preferSchemaTypePropertyDisallows Schema.Schema.Type<typeof X> in favor of typeof X.Type
redundantMapErrorSuggests hoisting a repeated trailing Effect.mapError from every yield in an Effect generator
redundantOrDieSuggests hoisting a repeated trailing Effect.orDie from every yield in an Effect generator
redundantSchemaTagIdentifierSuggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest
schemaNumberSuggests Schema.Finite and Schema.FiniteFromString instead of Schema.Number APIs when describing domain numbers
schemaStructWithTagSuggests using Schema.TaggedStruct instead of Schema.Struct with _tag field
schemaUnionOfLiteralsSuggests combining multiple Schema.Literal calls in Schema.Union into a single Schema.Literal
serviceNotAsClassWarns when Context.Service is used as a variable instead of a class declaration
strictBooleanExpressionsEnforces boolean types in conditional expressions for type safety
syncToSucceedSuggests using Effect.succeed instead of Effect.sync when the thunk returns a constant value
unnecessaryArrowBlockSuggests using a concise arrow body when the block only returns an expression
unnecessaryEffectGenSuggests removing Effect.gen when it contains only a single return statement
unnecessaryFailYieldableErrorSuggests yielding yieldable errors directly instead of wrapping with Effect.fail
unnecessaryPipeRemoves pipe calls with no arguments
unnecessaryPipeChainSimplifies chained pipe calls into a single pipe call
unnecessaryTypeofTypeSuggests replacing typeof Schema.Type style annotations with the matching named type when available

Refactor Status

Refactor V3 V4 Notes
asyncAwaitToFn Convert async/await to Effect.fn
asyncAwaitToFnTryPromise Convert async/await to Effect.fn with Error ADT + tryPromise
asyncAwaitToGen Convert async/await to Effect.gen
asyncAwaitToGenTryPromise Convert async/await to Effect.gen with Error ADT + tryPromise
debugPerformance Insert performance timing debug comments
effectGenToFn Convert Effect.gen to Effect.fn
functionToArrow Convert function declaration to arrow function
layerMagic Auto-compose layers with correct merge/provide
makeSchemaOpaque Convert Schema to opaque type aliases
makeSchemaOpaqueWithNs Convert Schema to opaque types with namespace
pipeableToDatafirst Convert pipeable calls to data-first style
removeUnnecessaryEffectGen Remove redundant Effect.gen wrapper
structuralTypeToSchema Generate recursive Schema from type alias
toggleLazyConst Toggle lazy/eager const declarations
togglePipeStyle Toggle pipe(x, f) vs x.pipe(f)
toggleReturnTypeAnnotation Add/remove return type annotation
toggleTypeAnnotation Add/remove variable type annotation
typeToEffectSchema Generate Effect.Schema from type alias
typeToEffectSchemaClass Generate Schema.Class from type alias
wrapWithEffectGen Wrap expression in Effect.gen
wrapWithPipe Wrap selection in pipe(...)
writeTagClassAccessors Generate static accessors for Effect.Service/Tag classes

Completion Status

Completion V3 V4 Notes
contextSelfInClasses Context.Tag self-type snippets in extends clauses (V3-only)
effectDataClasses Data class constructor snippets in extends clauses
effectSchemaSelfInClasses Schema/Model class constructor snippets in extends clauses
effectSelfInClasses Effect.Service/Effect.Tag self-type snippets in extends clauses (V3-only)
genFunctionStar gen(function*(){}) snippet when dot-accessing .gen on objects with callable gen property
effectCodegensComment @effect-codegens directive snippet in comments with codegen name choices
effectDiagnosticsComment @effect-diagnostics / @effect-diagnostics-next-line directive snippets in comments
rpcMakeClasses Rpc.make constructor snippet in extends clauses (V3-only)
schemaBrand brand("varName") snippet when dot-accessing Schema in variable declarations (V3-only)
serviceMapSelfInClasses Service map self-type snippets in extends clauses

Best Practices

Relationship to Official TypeScript-Go (tsgo)

Effect-tsgo is a superset of the official TypeScript-Go — it embeds a pinned version of tsgo with a small patch set on top and adds the Effect language service. This means effect-tsgo provides all standard TypeScript-Go functionality plus Effect-specific diagnostics, quick fixes, and refactors.

Use effect-tsgo instead of tsgo, not alongside it. Running both in parallel will produce duplicate diagnostics and degrade editor performance. Configure your editor to use effect-tsgo as your sole TypeScript language server.

Version Pinning

Each release of effect-tsgo is built against the upstream profiles recorded in _packages/tsgo/upstream.json. The Nix flake consumes the next profile directly. When upstream tsgo releases new features or fixes, effect-tsgo will adopt them in a subsequent release after validating compatibility with the Effect diagnostics layer.

When to Upgrade

  • Upgrade effect-tsgo when a new release includes upstream tsgo fixes you need or new Effect diagnostics you want.
  • There is no need to track upstream tsgo releases separately — effect-tsgo is the single binary to manage.

Plugin Options

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@effect/language-service",
        // Controls Effect refactors. (default: true)
        "refactors": true,
        // Controls Effect diagnostics. (default: true)
        "diagnostics": true,
        // When false, suggestion-level Effect diagnostics are omitted from tsc CLI output. (default: true)
        "includeSuggestionsInTsc": true,
        // Controls Effect quickinfo. (default: true)
        "quickinfo": true,
        // Controls Effect completions. (default: true)
        "completions": true,
        // Enables additional debug-only Effect language service output. (default: false)
        "debug": false,
        // Controls Effect goto references support. (default: true)
        "goto": true,
        // Controls Effect rename helpers. (default: true)
        "renames": true,
        // When true, suggestion diagnostics do not affect the tsc exit code. (default: true)
        "ignoreEffectSuggestionsInTscExitCode": true,
        // When true, warning diagnostics do not affect the tsc exit code. (default: false)
        "ignoreEffectWarningsInTscExitCode": false,
        // When true, error diagnostics do not affect the tsc exit code. (default: false)
        "ignoreEffectErrorsInTscExitCode": false,
        // When true, disabled diagnostics are still processed so directives can re-enable them. (default: false)
        "skipDisabledOptimization": false,
        // Mermaid rendering service for layer graph links. Accepts mermaid.live, mermaid.com, or a custom URL. (default: "mermaid.live")
        "mermaidProvider": "mermaid.live",
        // When true, suppresses external Mermaid links in hover output. (default: false)
        "noExternal": false,
        // How many levels deep the layer graph extraction follows symbol references. (default: 0)
        "layerGraphFollowDepth": 0,
        // When true, suppresses redundant return-type inlay hints on supported Effect generator functions. (default: false)
        "inlays": false,
        // Package names that should prefer namespace imports. (default: [])
        "namespaceImportPackages": [],
        // Package names that should prefer barrel named imports. (default: [])
        "barrelImportPackages": [],
        // Package-level import aliases keyed by package name. (default: {})
        "importAliases": {},
        // Controls whether named reexports are followed at package top-level. (default: "ignore")
        "topLevelNamedReexports": "ignore",
        // Configures key pattern formulas for the deterministicKeys rule. (default: [{"target":"service","pattern":"default","skipLeadingPath":["src/"]},{"target":"custom","pattern":"default","skipLeadingPath":["src/"]}])
        "keyPatterns": [
          {
            "target": "service",
            "pattern": "default",
            "skipLeadingPath": [
              "src/"
            ]
          },
          {
            "target": "custom",
            "pattern": "default",
            "skipLeadingPath": [
              "src/"
            ]
          }
        ],
        // Enables matching constructors with @effect-identifier annotations. (default: false)
        "extendedKeyDetection": false,
        // Minimum number of contiguous pipeable transformations to trigger missedPipeableOpportunity. (default: 2)
        "pipeableMinArgCount": 2,
        // Package names allowed to have multiple versions without triggering duplicatePackage. (default: [])
        "allowedDuplicatedPackages": [],
        // Controls which effectFnOpportunity quickfix variants are offered. (default: ["span"])
        "effectFn": [
          "span"
        ],
        // Maps rule names to severity levels. Use {} to enable diagnostics with rule defaults. (default: {})
        "diagnosticSeverity": {},
        // Ordered per-file diagnostic option overrides. (default: [{"include":["src/**/*.ts"],"options":{"diagnosticSeverity":{"floatingEffect":"error"}}}])
        "overrides": [
          {
            "include": [
              "src/**/*.ts"
            ],
            "options": {
              "diagnosticSeverity": {
                "floatingEffect": "error"
              }
            }
          }
        ]
      }
    ]
  }
}

About

TypeScript-go enhanced with the Effect LSP experience

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages