Linting TypeScript with ESLint and Prettier: A TypeScript-First Approach

Master automated code quality for TypeScript projects with professional tooling configuration

Modern TypeScript development demands more than just type checking. While TypeScript's compiler catches type-related errors at build time, it doesn't address the broader concerns of code quality, consistency, and best practices that distinguish professional codebases from amateur attempts. This is where a well-configured linting setup becomes essential.

ESLint and Prettier form the cornerstone of professional TypeScript code quality. ESLint analyzes your code for problematic patterns, stylistic inconsistencies, and potential bugs, while Prettier enforces consistent formatting without debate. Together, they create an automated quality gate that catches issues early and maintains codebase uniformity across teams of any size.

In this guide, we'll explore how to configure these tools specifically for TypeScript projects, leveraging the official typescript-eslint tooling to achieve type-aware linting that complements TypeScript's compile-time checks. Whether you're starting a new project or improving an existing codebase, the principles and configurations covered here will help you establish a solid foundation for maintainable TypeScript code.

Why Linting Matters for TypeScript

TypeScript has fundamentally changed how developers approach JavaScript development by adding compile-time type checking. However, type safety alone doesn't guarantee code quality. ESLint complements TypeScript's strengths by providing an additional layer of analysis that catches issues the type system cannot detect.

The key distinction between what TypeScript and ESLint catch is important: TypeScript ensures your code is structurally sound and types are used correctly, while ESLint enforces coding standards, identifies common mistakes, and promotes best practices. Together, they form a comprehensive quality assurance system that catches issues at different stages of development.

The TypeScript Linting Advantage

TypeScript brings compile-time type checking to JavaScript, catching entire classes of bugs before runtime. However, type safety alone doesn't address code quality concerns like consistent styling, potential anti-patterns, or best practices for maintainability. This is where ESLint complements TypeScript's strengths by providing an additional layer of code analysis that catches issues the type system cannot detect.

ESLint operates through a set of rules that analyze your code for problematic patterns, stylistic inconsistencies, and potential bugs. When configured properly for TypeScript, ESLint can understand your codebase at a deeper level, leveraging type information to provide more accurate and meaningful warnings. This combination creates a powerful defense-in-depth strategy for maintaining high-quality TypeScript codebases.

The key distinction between what TypeScript and ESLint catch is important: TypeScript ensures your code is structurally sound and types are used correctly, while ESLint enforces coding standards, identifies common mistakes, and promotes best practices. Together, they form a comprehensive quality assurance system that catches issues at different stages of development.

Prettier: Formatting Without Debate

Prettier takes a different approach from traditional linters by focusing exclusively on code formatting. Rather than allowing teams to debate formatting choices like brace placement or line length, Prettier enforces a consistent style automatically. This eliminates bike-shedding discussions and ensures all code in your project follows the same formatting rules.

The philosophy behind Prettier is that code formatting should be deterministic and automated. Instead of developers manually formatting code or arguing about style preferences, Prettier handles all formatting concerns consistently. This includes line width, indentation, semicolons, quotes, and spacing around operators.

For TypeScript projects, Prettier integrates seamlessly with the codebase. It understands TypeScript syntax and types, formatting them appropriately without breaking code semantics. The formatter respects your TypeScript configuration and produces output that matches your coding style preferences.

As noted in LogRocket's comprehensive guide to TypeScript linting, separating formatting from linting concerns allows each tool to do what it does best, resulting in a cleaner and more maintainable configuration.

Setting Up typescript-eslint

The typescript-eslint project provides the official ESLint tooling for TypeScript. This includes the parser that understands TypeScript syntax and the plugin that provides TypeScript-specific rules. For projects using ESLint's flat config format (introduced in ESLint 9), the setup process has been streamlined to reduce configuration complexity while maintaining flexibility.

Installing the Required Packages

The typescript-eslint project provides the essential tooling for linting TypeScript code with ESLint. This includes the parser that understands TypeScript syntax and the plugin that provides TypeScript-specific rules. The installation process involves adding these packages alongside ESLint itself.

For projects using ESLint's flat config format (the modern approach introduced in ESLint 9), you need to install several packages: eslint itself, @eslint/js for JavaScript recommended rules, typescript for type checking, and typescript-eslint which includes the parser and plugin packages.

According to the typescript-eslint.io official documentation, the recommended installation command pulls in all necessary dependencies. After installation, you configure ESLint to use the typescript-eslint parser and plugin, enabling the TypeScript-specific rules alongside ESLint's built-in JavaScript rules.

The flat config format uses JavaScript modules, typically in an eslint.config.mjs file, which provides better support for TypeScript and ES modules compared to the older JSON configuration format.

Understanding Configuration Levels

typescript-eslint provides three configuration levels that progressively increase in strictness: recommended, strict, and stylistic. Each level builds upon the previous one, adding more rules that catch potential issues or enforce additional style guidelines.

The recommended configuration serves as a starting point with a curated set of rules that catch common problems without being overly aggressive. These rules have been selected based on their effectiveness at catching real issues while minimizing false positives. Most teams begin with the recommended configuration and adjust from there.

The strict configuration extends recommended by adding rules that help identify potential bugs and enforce more consistent coding practices. These rules are more likely to flag code that works but could be improved. Teams focused on code quality often start with strict to establish higher standards from the beginning.

The stylistic configuration adds rules that primarily affect code formatting and style consistency. While some of these rules overlap with Prettier's functionality, they can be useful for projects that haven't fully integrated Prettier or want additional style enforcement beyond what Prettier provides. As noted in the Expo documentation on ESLint and Prettier, many teams choose to disable stylistic rules entirely when using Prettier to avoid conflicts.

Integrating Prettier with ESLint

ESLint and Prettier serve different but complementary purposes. ESLint focuses on code quality and potential bugs, while Prettier handles formatting consistency. When used together, they provide comprehensive code quality coverage without overlapping responsibilities.

The Formatting Overlap Problem

ESLint includes some rules that affect code formatting, such as enforcing or forbidding semicolons, quote style, and indentation. Prettier also handles these concerns, which creates potential for conflicts. Without proper configuration, ESLint and Prettier might disagree on formatting, leading to inconsistent results or endless formatting cycles.

The solution involves two parts: eslint-config-prettier turns off ESLint rules that Prettier handles, while eslint-plugin-prettier runs Prettier through ESLint as a rule. This combination allows you to use Prettier for formatting while still getting ESLint's reporting mechanism, making it easier to see formatting issues alongside other linting problems.

By disabling conflicting ESLint rules, you ensure that formatting is handled exclusively by Prettier. This creates a clear separation of concerns: ESLint catches code quality issues, and Prettier handles formatting. The integration through eslint-plugin-prettier then surfaces any Prettier violations through ESLint's output, maintaining a single tool for developers to check.

As documented in various setup guides including the DEV Community tutorial on TypeScript, ESLint, and Prettier integration, this configuration approach has become the standard for modern TypeScript projects.

Configuring the Integration

The integration requires adding eslint-config-prettier to your ESLint configuration after other configurations. This ensures its rule disabling takes effect, overriding any formatting rules that might have been enabled by other configurations like typescript-eslint or ESLint's built-in rules.

When properly configured, ESLint will report formatting issues through its normal output mechanisms. Developers can run a single command to check both code quality and formatting, simplifying the development workflow. Many teams configure their editors to run Prettier on save automatically, combining the formatting and linting steps into a seamless experience.

The key to successful integration is ensuring that eslint-config-prettier is loaded last in your configuration cascade. This guarantees that it properly disables any conflicting formatting rules from other configurations. Some teams also add eslint-plugin-prettier to get Prettier violations reported as ESLint errors, though this is optional.

Pre-commit hooks using tools like husky and lint-staged provide a final quality check before code enters version control. These hooks run formatting and linting checks on staged files, catching issues that might have been missed during development. This creates a safety net that prevents low-quality code from entering the codebase.

Best Practices for TypeScript Linting

Effective linting goes beyond initial configuration. The real value comes from establishing workflows and practices that make linting an integral part of your development process rather than an afterthought.

Choosing the Right Rule Set

Selecting appropriate rules for your TypeScript project depends on your team's experience level, project requirements, and code quality standards. Starting with typescript-eslint's recommended configuration provides a solid foundation that catches common issues without being overly restrictive.

For teams transitioning from JavaScript to TypeScript, the recommended configuration helps identify patterns that don't translate well to TypeScript's type system. This includes catching implicit any types, unused variables, and type assertions that could be improved. The rules provide educational value by explaining why certain patterns are discouraged.

Advanced teams often adopt the strict configuration to enforce higher standards. This catches more potential issues and encourages more defensive coding practices. The additional rules promote clearer code, reduce cognitive load when reading code, and help prevent subtle bugs that might otherwise go unnoticed.

Our frontend development services emphasize starting with the recommended configuration and gradually adopting stricter rules as teams become comfortable with the tooling. This approach balances immediate productivity with long-term code quality.

Organizing Your Configuration

For larger projects or monorepos, organizing ESLint configuration becomes increasingly important. The flat config format supports splitting configuration into multiple files and importing them, making it easier to manage complex setups. You can create separate config files for different parts of your project if they require different rules.

Environment-specific configurations can also be useful. For example, you might enable additional rules in CI that you temporarily disable during active development to reduce noise. Alternatively, you might have different rule sets for test files versus source files, since some patterns are acceptable in tests but not in production code.

Version controlling your ESLint configuration alongside your code ensures that all team members use the same setup. This consistency is essential for maintaining code quality standards across the team. When updating rules, consider the impact on existing code and plan for gradual adoption.

We recommend documenting your linting configuration decisions in your project's README, explaining why certain rules are enabled or disabled. This helps onboard new team members and provides context for future adjustments. For comprehensive build tooling guidance, explore our resources on modern frontend build processes.

Editor Integration Workflow

Modern editors integrate ESLint and Prettier seamlessly, providing real-time feedback as you write code. VS Code, through its ESLint and Prettier extensions, can automatically format code on save and highlight linting issues in the editor. This immediate feedback helps developers catch and fix issues early in the development process.

Configuring editor settings to run Prettier as the default formatter and enabling format on save eliminates manual formatting steps. Combined with ESLint's auto-fix capabilities, developers can maintain clean, consistent code with minimal effort. The key is setting up these integrations once during project setup and ensuring all team members have compatible editor configurations.

For teams working with TypeScript, editor integration is particularly valuable because it provides instant feedback on type-related linting rules. Seeing a warning about an implicit any type or an unused variable as you type allows for immediate correction, reducing the feedback loop significantly compared to waiting for CI checks to run.

Pre-commit hooks using tools like husky and lint-staged provide a final quality check before code enters version control. These hooks run formatting and linting checks on staged files, catching issues that might have been missed during development. This creates a safety net that prevents low-quality code from entering the codebase.

Common Configuration Examples

Understanding how to configure ESLint and Prettier for different scenarios helps you make informed decisions about your own setup. The following examples demonstrate practical configurations for TypeScript projects.

Minimal Starter Configuration

A minimal TypeScript ESLint configuration with Prettier integration provides the essential rules without being overwhelming. This configuration enables the typescript-eslint parser and uses the recommended configuration while disabling formatting rules in favor of Prettier.

The configuration imports necessary packages, sets up the TypeScript parser with appropriate options, and includes recommended rules from both ESLint and typescript-eslint. The prettier configuration comes last to disable any conflicting formatting rules, ensuring clean separation between linting and formatting responsibilities.

For teams new to linting, this minimal configuration provides immediate value with minimal setup. As the team becomes comfortable with linting, they can incrementally add more rules or configurations to address specific concerns. The key is starting with something workable and iterating based on real project needs.

This approach aligns with our philosophy at Digital Thrive: establish solid foundations first, then build upon them as understanding grows. Many teams make the mistake of enabling too many rules initially, which leads to frustration and disabled linting. Starting simple and expanding deliberately produces better long-term results.

Comprehensive Configuration with Strict Rules

A comprehensive configuration extends beyond the recommended rules to include strict rules that catch more potential issues. This includes rules for TypeScript-specific patterns, potential bugs, and code quality improvements. The configuration also includes careful customization of rules to match project requirements.

This level of configuration typically includes custom rule settings based on team preferences. For example, you might configure the @typescript-eslint/explicit-function-return-type rule to allow implicit returns in small arrow functions while requiring explicit returns elsewhere. These customizations help balance thoroughness with developer productivity.

The comprehensive configuration also considers special cases like test files, which might have different rule requirements than production code. By carefully structuring the configuration, you can apply appropriate rules to different file types without duplicating configuration or creating confusion.

When implementing a comprehensive configuration, we recommend enabling rules incrementally and addressing violations systematically. Adding too many strict rules at once can overwhelm teams and lead to rule disabling. A phased approach, perhaps adding one rule category at a time, produces better adoption and understanding. For teams building complex applications, pairing robust linting with proper web development practices creates a powerful foundation for maintainable codebases.

Advanced: Typed Linting with Type Information

Typed linting represents the next level of code quality enforcement, where ESLint rules can access TypeScript's type information for more accurate and sophisticated analysis. This capability significantly expands what linting can catch, going beyond syntax-level analysis to understand type relationships and potential issues.

Leveraging Type Information

Traditional linting operates on the syntax level, analyzing code structure without understanding type relationships. Typed linting takes this further by providing rules access to TypeScript's type information, enabling more accurate and sophisticated analysis. Rules can understand what types are used, how they're related, and identify issues that require type understanding.

For example, a rule checking for unreachable code can use type information to understand conditional branches more accurately. A rule preventing unsafe type assertions can analyze the actual types involved. These rules catch issues that syntax-only analysis would miss, providing deeper code quality insights.

Typed linting does require additional configuration, typically pointing ESLint to your tsconfig.json file. This allows the typescript-eslint parser to load type information and make it available to rules. The performance impact is generally acceptable for most projects, though very large codebases might notice slower linting times.

The trade-off between thoroughness and performance is worth considering. For most projects, the benefits of typed linting outweigh the slight increase in linting time. The ability to catch type-related issues that would otherwise slip through provides significant value, especially in larger codebases where subtle type errors can be difficult to spot during code review.

Rules That Benefit from Type Information

Several rules in typescript-eslint specifically leverage type information to provide more accurate analysis. The @typescript-eslint/restrict-template-expressions rule, for instance, can check what types are used in template literals, catching potential runtime errors when non-string types are interpolated without proper conversion.

The @typescript-eslint/no-unnecessary-type-assertion rule identifies type assertions that don't change the apparent type of an expression. These unnecessary assertions add noise without providing value, and typed linting can automatically detect and flag them. This rule is particularly valuable for teams working with legacy code that may contain vestigial type assertions.

Other type-aware rules include those that check for type safety in comparisons, identify unsafe function return types, and ensure proper type narrowing in conditional branches. These rules represent the next level of code quality enforcement, going beyond what traditional linting or type checking alone can achieve.

By combining these type-aware rules with your TypeScript compiler checks, you create a comprehensive safety net that catches issues at multiple levels. This multi-layered approach to code quality is a hallmark of professional TypeScript development and something we emphasize in all our frontend development services.

Maintaining Your Linting Setup

A linting configuration requires ongoing attention to remain effective. As projects evolve and tools update, your configuration should adapt accordingly. Regular maintenance ensures your linting setup continues to provide value rather than becoming a source of friction.

Keeping Current with Updates

ESLint, typescript-eslint, and Prettier receive regular updates that include new rules, bug fixes, and performance improvements. Staying current with these updates ensures you benefit from the latest analysis capabilities and security patches. However, updates can also introduce breaking changes or new rules that might require adjustment.

Following the release notes for these tools helps you understand what changes are coming and how they might affect your configuration. Major version upgrades typically have migration guides that explain necessary changes. Testing updates in a development branch before applying them to production prevents unexpected issues.

Automating dependency updates through tools like Dependabot or Renovate can help keep your linting dependencies current. These tools create pull requests for updates that you can review and merge. This approach balances keeping up-to-date with maintaining stability.

We recommend scheduling regular reviews of your linting configuration, perhaps quarterly, to evaluate whether current rules still serve your project's needs. Teams often add rules over time without removing those that no longer apply, leading to bloated configurations that slow down the linting process and create noise.

Adapting Configuration Over Time

As projects evolve, their linting needs change. A startup might start with strict rules to establish good habits, then relax some rules as the team grows. An enterprise project might add rules as it matures and code quality standards increase. Your linting configuration should adapt to these changing needs.

When adding new rules, consider the impact on developer productivity. Rules that generate many false positives create noise that drowns out important issues. Consider disabling or customizing rules that don't provide value in your specific context. The goal is catching real issues, not accumulating warnings that developers learn to ignore.

Code reviews can reveal patterns that should be caught by linting. When reviewers consistently point out the same issues, consider whether a linting rule could catch them automatically. This feedback loop between code review and linting configuration helps continuously improve code quality.

Our team has found that the most successful linting configurations evolve alongside the project, reflecting the team's growing understanding of code quality. Rather than implementing a comprehensive configuration upfront, we recommend starting with a solid foundation and iterating based on real-world experience. This approach produces configurations that genuinely improve code quality rather than becoming bureaucratic obstacles.

Common Questions About TypeScript Linting

Should I use Prettier and ESLint together?

Yes, Prettier and ESLint complement each other effectively. Prettier handles all formatting concerns, while ESLint focuses on code quality and potential bugs. Using eslint-config-prettier disables formatting rules in ESLint that would conflict with Prettier, creating a clean separation of concerns.

What configuration level should I start with?

Most teams should start with the recommended configuration from typescript-eslint. This provides a solid foundation without being overly restrictive. As your team becomes comfortable, you can incrementally add stricter rules based on your specific quality standards.

How do I enable type-aware linting?

Type-aware linting requires pointing your ESLint configuration to your TypeScript configuration file (tsconfig.json). In your eslint.config.mjs, you configure the TypeScript parser with the project option set to your tsconfig path. This enables rules that can leverage type information for more accurate analysis.

How often should I update my linting dependencies?

Regular updates are recommended, typically every few months. Enable automated dependency updates through tools like Dependabot to receive pull requests for new versions. Always test updates in a development branch before merging to production to catch any breaking changes.

Sources

  1. typescript-eslint.io - Getting Started - Official guide for ESLint flat config and TypeScript setup
  2. LogRocket Blog - Linting TypeScript ESLint Prettier - Comprehensive tutorial with examples
  3. Expo Documentation - Using ESLint and Prettier - Modern flat config setup guide
  4. DEV Community - Express API with TypeScript, ESLint, Prettier - 2025 setup guide with code examples

Need Help Setting Up TypeScript Linting?

Our frontend development team specializes in establishing professional development workflows with automated code quality tools. We can help you configure ESLint and Prettier for your TypeScript projects.