Skip to content

Latest commit

 

History

History
268 lines (233 loc) · 5.53 KB

File metadata and controls

268 lines (233 loc) · 5.53 KB
title Configure Linting for Effect
id tooling-linting
skillLevel intermediate
applicationPatternId tooling-and-debugging
summary Set up Biome or ESLint with Effect-specific rules for code quality.
tags
tooling
linting
biome
eslint
rule
description
Use Biome for fast linting with Effect-friendly configuration.
author PaulJPhilp
related
tooling-hello-world
tooling-ci-cd
lessonOrder 1

Guideline

Configure Biome (recommended) or ESLint with rules that work well with Effect's functional patterns.


Rationale

Good linting for Effect:

  1. Catches errors - Unused variables, missing awaits
  2. Enforces style - Consistent code across team
  3. Avoids antipatterns - No implicit any, proper typing
  4. Fast feedback - Errors in editor immediately

Good Example

1. Biome Configuration (Recommended)

// biome.json
{
  "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "complexity": {
        "noExcessiveCognitiveComplexity": "warn",
        "noForEach": "off",  // Effect uses forEach patterns
        "useLiteralKeys": "off"  // Effect uses computed keys
      },
      "correctness": {
        "noUnusedVariables": "error",
        "noUnusedImports": "error",
        "useExhaustiveDependencies": "warn"
      },
      "style": {
        "noNonNullAssertion": "warn",
        "useConst": "error",
        "noParameterAssign": "error"
      },
      "suspicious": {
        "noExplicitAny": "warn",
        "noConfusingVoidType": "off"  // Effect uses void
      },
      "nursery": {
        "noRestrictedImports": {
          "level": "error",
          "options": {
            "paths": {
              "lodash": "Use Effect functions instead",
              "ramda": "Use Effect functions instead"
            }
          }
        }
      }
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "javascript": {
    "formatter": {
      "semicolons": "asNeeded",
      "quoteStyle": "double",
      "trailingComma": "es5"
    }
  },
  "files": {
    "ignore": [
      "node_modules",
      "dist",
      "coverage",
      "*.gen.ts"
    ]
  }
}

2. ESLint Configuration (Alternative)

// eslint.config.js
import eslint from "@eslint/js"
import tseslint from "typescript-eslint"

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.strictTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        project: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
    rules: {
      // TypeScript strict rules
      "@typescript-eslint/no-unused-vars": [
        "error",
        { argsIgnorePattern: "^_" }
      ],
      "@typescript-eslint/no-explicit-any": "warn",
      "@typescript-eslint/explicit-function-return-type": "off",
      "@typescript-eslint/no-floating-promises": "error",

      // Effect-friendly rules
      "@typescript-eslint/no-confusing-void-expression": "off",
      "@typescript-eslint/no-misused-promises": [
        "error",
        { checksVoidReturn: false }
      ],

      // Style rules
      "prefer-const": "error",
      "no-var": "error",
      "object-shorthand": "error",
      "prefer-template": "error",
    },
  },
  {
    files: ["**/*.test.ts"],
    rules: {
      "@typescript-eslint/no-explicit-any": "off",
    },
  },
  {
    ignores: ["dist/", "coverage/", "node_modules/"],
  }
)

3. Package.json Scripts

{
  "scripts": {
    "lint": "biome check .",
    "lint:fix": "biome check --apply .",
    "lint:ci": "biome ci .",
    "format": "biome format --write .",
    "format:check": "biome format ."
  }
}

4. VS Code Integration

// .vscode/settings.json
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  }
}

5. Pre-commit Hook

// package.json
{
  "scripts": {
    "prepare": "husky"
  }
}
# .husky/pre-commit
bun run lint:ci
bun run typecheck

6. Effect-Specific Rules to Consider

// Custom rules you might want

// ❌ Bad: Using Promise where Effect should be used
{
  const fetchData = async () => { }  // Warn in Effect codebase
}

// ✅ Good: Using Effect
{
  const fetchData = Effect.gen(function* () { })
}

// ❌ Bad: Throwing errors
{
  const validate = (x: unknown) => {
    if (!x) throw new Error("Invalid")  // Error
  }
}

// ✅ Good: Returning Effect with error
{
  const validate = (x: unknown) =>
    x ? Effect.succeed(x) : Effect.fail(new ValidationError())
}

// ❌ Bad: Using null/undefined directly
{
  const maybeValue: string | null = null  // Warn
}

// ✅ Good: Using Option
{
  const maybeValue: Option.Option<string> = Option.none()
}

Biome vs ESLint

Feature Biome ESLint
Speed ⚡ Very fast 🐢 Slower
Config Simple JSON Complex
Plugins Limited Many
Formatting Built-in Need Prettier

Best Practices

  1. Use Biome for new projects - Faster, simpler
  2. Enable strict rules - Catch more bugs
  3. Auto-fix on save - Reduce friction
  4. Run in CI - Enforce standards
  5. Ignore generated files - Don't lint build output