An extensible static analysis linter for the TypeScript language
—
TSLint provides comprehensive configuration management with support for multiple file formats, inheritance, and programmatic configuration loading.
import { Configuration } from 'tslint';
// Find configuration relative to a file
function findConfiguration(
configFile: string | null,
inputFilePath: string
): IConfigurationLoadResult
// Find configuration file path
function findConfigurationPath(
suppliedConfigFilePath: string | null,
inputFilePath: string
): string | undefinedinterface IConfigurationLoadResult {
path?: string;
results?: IConfigurationFile;
}Example:
import { Configuration } from 'tslint';
// Find configuration for a specific file
const configResult = Configuration.findConfiguration(
'./tslint.json', // Config file path or undefined for auto-discovery
'./src/components/App.tsx' // File being linted
);
if (configResult.results) {
console.log(`Configuration loaded from: ${configResult.path}`);
console.log(`Rules defined: ${configResult.results.rules.size}`);
} else {
console.log('No configuration found, using defaults');
}// Load configuration from specific path
function loadConfigurationFromPath(configFilePath: string): IConfigurationLoadResultExample:
// Load specific configuration file
const config = Configuration.loadConfigurationFromPath('./custom-tslint.json');
if (config.results) {
// Use the configuration for linting
linter.lint(fileName, source, config.results);
}interface IConfigurationFile {
extends: string[];
jsRules: Map<string, Partial<IOptions>>;
linterOptions?: {
exclude: string[];
format: string;
};
rulesDirectory: string[];
rules: Map<string, Partial<IOptions>>;
}interface IOptions {
ruleArguments: any[];
ruleSeverity: "warning" | "error" | "off";
ruleName: string;
disabledIntervals: IDisabledInterval[];
}type RawRuleConfig = null | undefined | boolean | any[] | {
severity?: RuleSeverity | "warn" | "none" | "default";
options?: any;
};
type RuleSeverity = "warning" | "error" | "off";TSLint supports multiple configuration file formats:
{
"extends": ["tslint:recommended"],
"rulesDirectory": ["./custom-rules"],
"rules": {
"no-console": "error",
"quotemark": [true, "single"],
"max-line-length": {
"options": [120]
}
},
"jsRules": {
"no-console": "warning"
},
"linterOptions": {
"exclude": ["node_modules/**", "dist/**"],
"format": "stylish"
}
}extends:
- tslint:recommended
rulesDirectory:
- ./custom-rules
rules:
no-console: error
quotemark:
- true
- single
max-line-length:
options: [120]
jsRules:
no-console: warning
linterOptions:
exclude:
- "node_modules/**"
- "dist/**"
format: stylishmodule.exports = {
extends: ['tslint:recommended'],
rulesDirectory: ['./custom-rules'],
rules: {
'no-console': 'error',
'quotemark': [true, 'single'],
'max-line-length': { options: [120] }
},
jsRules: {
'no-console': 'warning'
},
linterOptions: {
exclude: ['node_modules/**', 'dist/**'],
format: 'stylish'
}
};// Configuration file names (in order of precedence)
const CONFIG_FILENAMES = ["tslint.json", "tslint.yaml", "tslint.yml"];
const JSON_CONFIG_FILENAME = "tslint.json";
// Default configurations
const DEFAULT_CONFIG: IConfigurationFile;
const EMPTY_CONFIG: IConfigurationFile;// Read and parse configuration file
function readConfigurationFile(filepath: string): RawConfigFileExample:
import { Configuration } from 'tslint';
// Read raw configuration
const rawConfig = Configuration.readConfigurationFile('./tslint.json');
console.log('Raw configuration:', rawConfig);
// Parse into normalized format
const parsedConfig = Configuration.parseConfigFile(
rawConfig,
'./project-root',
Configuration.readConfigurationFile
);function parseConfigFile(
configFile: RawConfigFile,
configFileDir: string,
readConfig: (path: string) => RawConfigFile
): IConfigurationFileinterface RawConfigFile {
extends?: string | string[];
linterOptions?: IConfigurationFile["linterOptions"];
rulesDirectory?: string | string[];
defaultSeverity?: string;
rules?: RawRulesConfig;
jsRules?: RawRulesConfig | boolean;
}
type RawRulesConfig = { [ruleName: string]: RawRuleConfig };// Convert raw rule configuration to normalized options
function convertRuleOptions(ruleConfiguration: Map<string, RawRuleConfig>): IOptions[]// Extend one configuration with another
function extendConfigurationFile(
targetConfig: IConfigurationFile,
nextConfigSource: IConfigurationFile
): IConfigurationFileExample:
import { Configuration } from 'tslint';
// Load base configuration
const baseConfig = Configuration.loadConfigurationFromPath('./base-tslint.json');
const projectConfig = Configuration.loadConfigurationFromPath('./tslint.json');
if (baseConfig.results && projectConfig.results) {
// Merge configurations (project config overrides base)
const mergedConfig = Configuration.extendConfigurationFile(
baseConfig.results,
projectConfig.results
);
}Common preset configurations that can be extended:
{
"extends": [
"tslint:recommended",
"tslint:latest",
"tslint:all"
]
}Available Presets:
tslint:recommended - Recommended rules for most projectstslint:latest - Latest recommended rulestslint:all - All available rules (very strict)// Get resolved rules directories
function getRulesDirectories(
directories: string | string[] | undefined,
relativeTo: string
): string[]Example:
import { Configuration } from 'tslint';
// Resolve rules directories relative to config file
const rulesDirs = Configuration.getRulesDirectories(
['./custom-rules', '../shared-rules'],
'/project/root'
);
console.log('Resolved rules directories:', rulesDirs);
// Output: ['/project/root/custom-rules', '/project/shared-rules']// Check if file should be excluded from linting
function isFileExcluded(filepath: string, configFile: IConfigurationFile): booleanExample:
import { Configuration } from 'tslint';
const config = Configuration.loadConfigurationFromPath('./tslint.json').results;
if (config) {
const shouldSkip = Configuration.isFileExcluded('./node_modules/lib.ts', config);
console.log('File excluded:', shouldSkip); // true if in exclude patterns
}// Convert configuration to JSON string
function stringifyConfiguration(configFile: IConfigurationFile): stringExample:
import { Configuration } from 'tslint';
const config = Configuration.loadConfigurationFromPath('./tslint.json').results;
if (config) {
// Serialize configuration back to JSON
const jsonString = Configuration.stringifyConfiguration(config);
console.log('Serialized config:', jsonString);
// Save modified configuration
fs.writeFileSync('./modified-tslint.json', jsonString);
}import { Configuration, IConfigurationFile } from 'tslint';
import * as path from 'path';
class ConfigurationManager {
private configCache = new Map<string, IConfigurationFile>();
loadConfig(filePath: string): IConfigurationFile | undefined {
const configDir = path.dirname(filePath);
// Check cache
if (this.configCache.has(configDir)) {
return this.configCache.get(configDir);
}
// Load configuration
const configResult = Configuration.findConfiguration(undefined, filePath);
if (configResult.results) {
this.configCache.set(configDir, configResult.results);
return configResult.results;
}
return undefined;
}
clearCache(): void {
this.configCache.clear();
}
}import { Configuration, IConfigurationFile } from 'tslint';
function validateConfiguration(config: IConfigurationFile): string[] {
const errors: string[] = [];
// Validate extends
config.extends.forEach(extend => {
if (!extend.startsWith('tslint:') && !fs.existsSync(extend)) {
errors.push(`Extended configuration not found: ${extend}`);
}
});
// Validate rules directories
config.rulesDirectory.forEach(dir => {
if (!fs.existsSync(dir)) {
errors.push(`Rules directory not found: ${dir}`);
}
});
// Validate rule configurations
config.rules.forEach((options, ruleName) => {
if (options.ruleSeverity && !['error', 'warning', 'off'].includes(options.ruleSeverity)) {
errors.push(`Invalid severity for rule ${ruleName}: ${options.ruleSeverity}`);
}
});
return errors;
}import { Configuration, IConfigurationFile } from 'tslint';
function mergeConfigurations(
configs: IConfigurationFile[]
): IConfigurationFile {
return configs.reduce((merged, config) =>
Configuration.extendConfigurationFile(merged, config),
Configuration.EMPTY_CONFIG
);
}
// Example: Project-specific configuration hierarchy
function loadProjectConfig(projectPath: string): IConfigurationFile {
const configs: IConfigurationFile[] = [];
// 1. Load global config
const globalConfig = Configuration.loadConfigurationFromPath('~/.tslint.json');
if (globalConfig.results) {
configs.push(globalConfig.results);
}
// 2. Load team config
const teamConfig = Configuration.loadConfigurationFromPath('./team-tslint.json');
if (teamConfig.results) {
configs.push(teamConfig.results);
}
// 3. Load project config
const projectConfig = Configuration.loadConfigurationFromPath(
path.join(projectPath, 'tslint.json')
);
if (projectConfig.results) {
configs.push(projectConfig.results);
}
return mergeConfigurations(configs);
}import { Configuration, IConfigurationFile } from 'tslint';
function loadEnvironmentConfig(environment: string): IConfigurationFile | undefined {
const configFiles = [
`./tslint.${environment}.json`,
`./configs/tslint-${environment}.json`,
'./tslint.json' // fallback
];
for (const configFile of configFiles) {
if (fs.existsSync(configFile)) {
const config = Configuration.loadConfigurationFromPath(configFile);
if (config.results) {
return config.results;
}
}
}
return undefined;
}
// Usage
const config = loadEnvironmentConfig(process.env.NODE_ENV || 'development');import { Configuration, Linter } from 'tslint';
import * as express from 'express';
// REST API for configuration management
const app = express();
app.get('/api/config/:project', (req, res) => {
const projectPath = req.params.project;
const configResult = Configuration.findConfiguration(
undefined,
path.join('./projects', projectPath, 'src/index.ts')
);
if (configResult.results) {
res.json({
path: configResult.path,
config: Configuration.stringifyConfiguration(configResult.results)
});
} else {
res.status(404).json({ error: 'Configuration not found' });
}
});
app.post('/api/validate-config', (req, res) => {
try {
const rawConfig = req.body;
const config = Configuration.parseConfigFile(
rawConfig,
'./temp',
Configuration.readConfigurationFile
);
res.json({ valid: true, config });
} catch (error) {
res.json({ valid: false, error: error.message });
}
});Install with Tessl CLI
npx tessl i tessl/npm-tslint