# How to Migrate TSLint Rules to TSSLint Using `importTSSLintRules`

> Easily migrate TSLint rules to TSSLint with importTSLintRules. Reuse your existing rule set without rewriting code. Learn the simple process in this guide from johnsoncodehk/tsslint.

- Repository: [Johnson Chu/tsslint](https://github.com/johnsoncodehk/tsslint)
- Tags: migration-guide
- Published: 2026-03-04

---

**The `importTSLintRules` function in `@tsslint/config` wraps existing TSLint rule classes and exposes them as native TSSLint rules, allowing you to reuse your current rule set without rewriting any code.**

TSSLint is a TypeScript-aware linter that runs as a language service plugin, offering faster feedback than traditional CLI-based tools. If you are moving from a legacy TSLint setup, the `johnsoncodehk/tsslint` repository provides a dedicated compatibility layer—centered on `importTSLintRules`—that bridges the two ecosystems. This article explains the internal mechanics of that bridge and provides a concrete migration workflow.

## How `importTSLintRules` Works Internally

The migration helper lives in [[`packages/config/lib/tslint.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/config/lib/tslint.ts)](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/tslint.ts#L18-L55). When you invoke `importTSLintRules`, it executes a three-phase pipeline to transform TSLint constructs into TSSLint-compatible functions.

### Rule Discovery via `getTSLintRulesDirectories`

First, the system locates any custom rule directories defined in your existing [`tslint.json`](https://github.com/johnsoncodehk/tsslint/blob/main/tslint.json). The helper [`getTSLintRulesDirectories`](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/tslint.ts#L56-L87) walks up the file tree from the current working directory, searches for a [`tslint.json`](https://github.com/johnsoncodehk/tsslint/blob/main/tslint.json) file, and extracts the `rulesDirectory` array. This ensures that third-party TSLint plugins installed in your project remain discoverable without manual path configuration.

### Rule Loading via `loadTSLintRule`

Next, for every rule name specified in your configuration, [`loadTSLintRule`](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/tslint.ts#L90-L121) attempts to resolve the corresponding compiled JavaScript file. It searches in the following order:

1. Custom directories discovered in the previous step (e.g., [`my-tslint-rules/NoConsoleRule.js`](https://github.com/johnsoncodehk/tsslint/blob/main/my-tslint-rules/NoConsoleRule.js))
2. The built-in TSLint rule folder (`node_modules/tslint/lib/rules`)

Once found, the file is `require`-d and the rule class is returned for instantiation.

### API Translation via `convertRule`

Finally, the raw TSLint class is passed to [`convertRule`](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/tslint.ts#L123-L187). This wrapper:

- Instantiates the TSLint rule with the options provided in your configuration
- Translates the TSLint `apply` or `applyWithProgram` methods into TSSLint’s `report` API
- Maps TSLint severities (`"error"`, `"warning"`, `"off"`) to TypeScript diagnostic categories using `normalizeRuleSeverity` from [[`packages/config/lib/utils.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/config/lib/utils.ts)](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/utils.ts)
- Forwards any automatic fixes via `reporter.withFix` so editors can offer quick-fixes

The result is a plain object where keys are rule identifiers and values are executable TSSLint rule functions. You spread this object directly into your TSSLint configuration.

## Step-by-Step Migration Process

Follow these steps to port an existing TSLint setup into TSSLint without code changes:

1. **Ensure TSLint is installed**  
   Keep `tslint` in your `node_modules` so the loader can resolve built-in rules like `no-console` or `member-ordering`.

2. **Preserve your [`tslint.json`](https://github.com/johnsoncodehk/tsslint/blob/main/tslint.json)**  
   Do not delete your existing configuration. The file must remain accessible (project root or parent directory) so `getTSLintRulesDirectories` can read the `rulesDirectory` field if you use custom plugins.

3. **Install TSSLint dependencies**  
   Add the configuration package (and CLI if desired):
   ```bash
   npm install --save-dev @tsslint/config @tsslint/cli
   ```

4. **Create [`tsslint.config.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/tsslint.config.ts)**  
   Import `importTSLintRules` and spread its output into the `rules` map:
   ```typescript
   import { defineConfig, importTSLintRules } from '@tsslint/config';

   export default defineConfig({
     rules: {
       // Spread the converted TSLint rules
       ...(await importTSLintRules({
         'no-console': true,
         'member-ordering': [true, { order: 'fields-first' }],
       })),
       
       // Add native TSSLint rules here if needed
     },
   });
   ```

5. **Run TSSLint**  
   Execute the linter via the CLI or as a TypeScript language service plugin:
   ```bash
   npx tsslint --project tsconfig.json
   ```

## Configuration Examples

### Legacy [`tslint.json`](https://github.com/johnsoncodehk/tsslint/blob/main/tslint.json) (Optional)

You only need this file if you rely on custom rule directories. The `rules` section inside it is not consumed directly, but keeping it helps document the original settings.

```json
{
  "rulesDirectory": ["my-tslint-rules"],
  "rules": {
    "no-console": true,
    "member-ordering": [true, { "order": "fields-first" }]
  }
}

```

### Modern [`tsslint.config.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/tsslint.config.ts)

This is the entry point TSSLint reads at runtime. The `importTSLintRules` call accepts an object matching the TSLint `rules` schema.

```typescript
import { defineConfig, importTSLintRules } from '@tsslint/config';

export default defineConfig({
  rules: {
    ...(await importTSLintRules({
      'no-console': true,
      'member-ordering': [true, { order: 'fields-first' }],
    })),
  },
});

```

## Key Architectural Details

**Severity Conversion**  
When `importTSLintRules` processes a rule, it calls `normalizeRuleSeverity` to convert TSLint’s string levels into internal `RuleSeverity` values. If a rule is set to `"off"`, the wrapper inserts a no-op placeholder rather than omitting the key entirely, preserving the configuration structure.

**Performance Caching**  
The converted rule functions are automatically cached by TSSLint’s core unless they access `ctx.program` (type-aware rules). This mirrors TSLint’s behavior while maintaining the high-performance promise of the TSSLint engine.

**Fix Translation**  
If the original TSLint rule provides a fix via `failure.hasFix()`, the wrapper translates it into a TSSLint fix object that integrates with the TypeScript Language Service, allowing editors to apply the suggestion with a single click.

## Summary

- **`importTSLintRules`** in `@tsslint/config` provides a zero-rewrite migration path for TSLint rules.
- The system discovers custom directories via `getTSLintRulesDirectories`, loads rule classes via `loadTSLintRule`, and converts them via `convertRule` in [`packages/config/lib/tslint.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/config/lib/tslint.ts).
- Severity levels and automatic fixes are mapped transparently to TypeScript diagnostics.
- You consume the result by spreading the returned object into the `rules` field of [`tsslint.config.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/tsslint.config.ts).
- Rules are cached for performance unless they require the TypeScript program instance.

## Frequently Asked Questions

### Do I need to rewrite my custom TSLint rules to use them in TSSLint?

No. As long as your custom rules follow the standard TSLint class interface (exporting a class with `apply` or `applyWithProgram` methods), `importTSLintRules` will wrap them automatically. Place them in a directory listed under `rulesDirectory` in your [`tslint.json`](https://github.com/johnsoncodehk/tsslint/blob/main/tslint.json) so the loader can find them.

### What happens if a TSLint rule is disabled in the configuration?

If you pass a rule with severity `"off"` (or `false`), `importTSLintRules` still includes the key in the output object but assigns it a no-op function. This ensures the rule name remains present in the configuration object while effectively disabling execution, matching TSLint’s behavior.

### Can I mix native TSSLint rules with imported TSLint rules?

Yes. The object returned by `importTSLintRules` is a standard JavaScript object that you can spread into the `rules` map. You can add native TSSLint rules before or after the spread, and they will coexist in the same linting pass.

### Does the migration support TSLint’s type-aware rules?

Yes. Rules that use `applyWithProgram` are detected and wrapped correctly. However, because they access `ctx.program`, they bypass the automatic caching layer and run on every program change, similar to how they behaved in the original TSLint architecture.