# TypeScript-ESLint v8 Migration Guide

Migrating to v7.0.0+ of `@bitfactory/eslint-config` (includes TypeScript-ESLint v8):

## Overview

Version 7.0.0 of `@bitfactory/eslint-config` updates TypeScript-ESLint from v7 to v8. TypeScript-ESLint v8 introduces several breaking changes and improvements that enhance type-safety and linting accuracy for TypeScript projects.

**Key Changes:**

- Several rules have been deprecated and replaced with better alternatives
- The controversial `ban-types` rule has been split into four targeted rules
- New type-aware rules provide better error detection
- Improved performance with the new Project Service feature

## What's Changed in This Config

### Rules from `flat/recommended` Preset

The `flat/recommended` preset (which this config uses) now includes new rules that replace `ban-types`:

- `@typescript-eslint/no-empty-object-type` - Prevents confusing use of `{}`
- `@typescript-eslint/no-unsafe-function-type` - Bans the overly broad `Function` type
- `@typescript-eslint/no-wrapper-object-types` - Bans `Object`, `Number`, `String`, etc.
- `@typescript-eslint/no-restricted-types` - configurable type bans, shipped with no defaults

The first three are enabled by the `flat/recommended` preset this config extends. `no-restricted-types` is
not in any preset and stays off until you configure it with your own list. Together they replace the old
`ban-types` rule with more granular control.

## Migration Steps

### If You're Already on v7.0.0+

No action required! Your configuration already includes TypeScript-ESLint v8.

### If You're Upgrading from v6.x or Earlier

1. **Update to v7.0.0**

   ```bash
   # Using pnpm (recommended)
   pnpm add @bitfactory/eslint-config@^7.0.0 --save-dev --save-exact

   # Using npm
   npm install @bitfactory/eslint-config@^7.0.0 --save-dev --save-exact
   ```

2. **Update peer dependencies**

   If your project doesn't use `.npmrc` with `auto-install-peers=true`, update peer dependencies:

   ```bash
   # Using pnpm
   pnpm dlx install-peerdeps --dev --extra-args="-E" @bitfactory/eslint-config@^7.0.0

   # Using npm
   npx install-peerdeps --dev --extra-args="-E" @bitfactory/eslint-config@^7.0.0
   ```

   The version is pinned because `install-peerdeps` reinstalls the package itself as well as its peers, so an
   unpinned command would resolve `latest` and undo the previous step. This will install:
   - `@typescript-eslint/eslint-plugin@>=8.0.0`
   - `@typescript-eslint/parser@>=8.0.0`
   - Other required peer dependencies

3. **Review your custom TypeScript rules**

   If you have custom overrides in your `eslint.config.js`, check for deprecated rules:

   **Renamed and removed rules** (replace these if you're using them):

   | Old Rule | New Rule | Status in 8.x |
   | ---------- | ---------- | ---------- |
   | `@typescript-eslint/prefer-ts-expect-error` | `@typescript-eslint/ban-ts-comment` | deprecated, still resolves |
   | `@typescript-eslint/no-var-requires` | `@typescript-eslint/no-require-imports` | deprecated, still resolves |
   | `@typescript-eslint/no-throw-literal` | `@typescript-eslint/only-throw-error` | **removed** |
   | `@typescript-eslint/ban-types` | Split into 4 rules (see above) | **removed** |

   The two marked removed are gone from the plugin, so a config that still names one fails to load with
   `Could not find "<rule>" in plugin "@typescript-eslint"` - a startup failure rather than a warning.

   An `eslint-disable` comment naming a removed rule fails the same way, one file at a time rather than at
   startup, so a config you have already cleaned can still be hiding one. Search your source for the removed
   names and delete the comments along with the config entries:

   ```bash
   grep -rn "eslint-disable.*@typescript-eslint/\(ban-types\|no-throw-literal\)" \
       --include='*.ts' --include='*.mts' --include='*.cts' --include='*.tsx' --include='*.vue' .
   ```

   **Example - Before:**

   ```javascript
   export default [
       ...bitfactoryTypeScript,
       {
           files: ['**/*.ts'],
           rules: {
               '@typescript-eslint/no-throw-literal': 'error', // Removed in 8.x - fails to load
           },
       },
   ];
   ```

   **Example - After:**

   ```javascript
   export default [
       ...bitfactoryTypeScript,
       {
           files: ['**/*.ts'],
           rules: {
               // only-throw-error is type-aware, so it needs type information enabled
               '@typescript-eslint/only-throw-error': 'error',
           },
       },
   ];
   ```

4. **Test your configuration**

   Run ESLint to check for any new errors from the updated rules:

   ```bash
   # Using pnpm
   pnpm run lint

   # Using npm
   npm run lint
   ```

   Some code that was previously allowed may now be flagged. These are typically genuine issues that TypeScript-ESLint v8 can now detect.

## Common Migration Issues

### Issue 1: Empty Object Type `{}`

**Problem:** The `{}` type in certain contexts is now flagged:

```typescript
// Now flagged
function acceptAnything(value: {}) {
    // ...
}
```

**Solution:** Use a more specific type:

```typescript
// Use object for non-null objects
function acceptAnything(value: object) {
    // ...
}

// Or use unknown for truly anything
function acceptAnything(value: unknown) {
    // ...
}

// Or use Record for key-value objects
function acceptAnything(value: Record<string, unknown>) {
    // ...
}
```

### Issue 2: `Function` Type

**Problem:** The overly broad `Function` type is now disallowed:

```typescript
// Now flagged
function callCallback(callback: Function) {
    callback();
}
```

**Solution:** Use a specific function signature:

```typescript
// Better: specific signature
function callCallback(callback: () => void) {
    callback();
}

// Or use a generic if you need flexibility
function callCallback<T extends (...args: never[]) => unknown>(callback: T) {
    callback();
}
```

## New Features You Can Adopt

### Project Service (Type-Aware Linting)

TypeScript-ESLint v8 introduces a stable Project Service feature that makes type-aware linting easier and
faster. This config does not enable it; opting in is documented in
[Configuration: Enable Type-Aware Linting](02-configuration.md#enable-type-aware-linting-typescript).

## Vue + TypeScript Projects

On Vue 3, use the `/vue3` entrypoint: it wires `@typescript-eslint/parser` as the `<script>` sub-parser
itself, so `<script setup lang="ts">` needs no parser block of your own. Pair it with `/typescript` so `.ts`
files are linted too, `/vue3` last. See
[Configuration: Vue 3, Nuxt and Laravel Inertia](02-configuration.md#vue-3-nuxt-and-laravel-inertia).

From `14.0.0` the `/vue` entrypoint supplies the TypeScript sub-parser itself, so a `<script lang="ts">`
block needs nothing wired by hand (ADR-0026). On `13.x` and earlier it did not. That
composition is in
[Configuration: Vue 2 (support deprecated)](02-configuration.md#vue-2-support-deprecated).

## Version Requirements

To use v7.0.0 of this config, you need:

- **Node.js:** `^20.9.0 || ^22.11.0 || ^24.11.0`
- **ESLint:** `^9.29.0`
- **TypeScript-ESLint:** `>=8.0.0`
- **TypeScript:** `>=4.8.4` (recommended: latest 5.x)

## Additional Resources

- [TypeScript-ESLint v8 Announcement](https://typescript-eslint.io/blog/announcing-typescript-eslint-v8/)
- [TypeScript-ESLint Rules Documentation](https://typescript-eslint.io/rules/)
- [Configuration Guide](02-configuration.md)
- [Flat Config Migration Guide](migration-flat-config.md)
