How to Migrate TSLint Rules to TSSLint Using `importTSSLintRules`
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/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. The helper getTSLintRulesDirectories walks up the file tree from the current working directory, searches for a 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 attempts to resolve the corresponding compiled JavaScript file. It searches in the following order:
- Custom directories discovered in the previous step (e.g.,
my-tslint-rules/NoConsoleRule.js) - 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. This wrapper:
- Instantiates the TSLint rule with the options provided in your configuration
- Translates the TSLint
applyorapplyWithProgrammethods into TSSLint’sreportAPI - Maps TSLint severities (
"error","warning","off") to TypeScript diagnostic categories usingnormalizeRuleSeverityfrom [packages/config/lib/utils.ts](https://github.com/johnsoncodehk/tsslint/blob/master/packages/config/lib/utils.ts) - Forwards any automatic fixes via
reporter.withFixso 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:
-
Ensure TSLint is installed
Keeptslintin yournode_modulesso the loader can resolve built-in rules likeno-consoleormember-ordering. -
Preserve your
tslint.json
Do not delete your existing configuration. The file must remain accessible (project root or parent directory) sogetTSLintRulesDirectoriescan read therulesDirectoryfield if you use custom plugins. -
Install TSSLint dependencies
Add the configuration package (and CLI if desired):npm install --save-dev @tsslint/config @tsslint/cli -
Create
tsslint.config.ts
ImportimportTSLintRulesand spread its output into therulesmap: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 }, }); -
Run TSSLint
Execute the linter via the CLI or as a TypeScript language service plugin:npx tsslint --project tsconfig.json
Configuration Examples
Legacy 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.
{
"rulesDirectory": ["my-tslint-rules"],
"rules": {
"no-console": true,
"member-ordering": [true, { "order": "fields-first" }]
}
}
Modern tsslint.config.ts
This is the entry point TSSLint reads at runtime. The importTSLintRules call accepts an object matching the TSLint rules schema.
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
importTSLintRulesin@tsslint/configprovides a zero-rewrite migration path for TSLint rules.- The system discovers custom directories via
getTSLintRulesDirectories, loads rule classes vialoadTSLintRule, and converts them viaconvertRuleinpackages/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
rulesfield oftsslint.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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →