Creating a TypeScript Style Guide Your Team Will Actually Follow
AI generated
<T>
type
TypeScript · Style Guide · Conventions
Creating a TypeScript Style Guide the Team Actually Follows
from a dead wiki page to a lived standard

Most TypeScript style guides end up as a long wiki page, written once and never read again. A style guide that actually works defines a clear scope, enforces rules automatically with ESLint and shows conventions through real examples instead of abstract prose.

16 min readESLint · Prettier · conventions · examplesTypeScript 5.x · ESLint 9 · flat config

1. Why Most Style Guides Fail

A TypeScript style guide rarely fails due to lack of willingness, but because of the form in which it is created. A one off wiki document with long prose paragraphs gets read when it is written and never again afterward, because it plays no role in daily work. New rules end up in pull request comments instead of flowing into the style guide, and the gap between document and practice grows with every sprint.

An effective TypeScript style guide differs from a dead wiki page in three ways. It gets enforced automatically instead of merely recommended. It shows examples instead of formulating rules in prose. And it lives in the repository, versioned alongside the code, instead of gathering dust in a separate documentation system.

2. Defining Scope: What a Style Guide Should Cover

The most common mistake when creating a TypeScript style guide is too broad a scope. A guide that wants to cover formatting, architecture and business conventions all at once becomes unwieldy and nobody reads it in full. Formatting questions like indentation or quote style do not belong in a style guide, they belong in the Prettier configuration, where they get applied automatically without discussion.

A TypeScript style guide should focus on decisions Prettier cannot make automatically: when to use interface instead of type alias, how generics get named, which export patterns apply to modules, and how any gets handled. That narrows the guide down to what actually needs discussion and convention, instead of repeating formatting questions a tool already solves.


{
  "//": "prettier.config.json — handles formatting so the style guide doesn't have to",
  "semi": true,
  "singleQuote": false,
  "trailingComma": "all",
  "printWidth": 100,
  "tabWidth": 2
}

3. Enforcing Rules Automatically Instead of Just Writing Them Down

A rule that only exists in the style guide but is not automatically checked disappears from the team's collective memory after a few weeks. Every rule in the TypeScript style guide should therefore answer the question of how it can be enforced with ESLint or a custom rule. Rules that cannot be automated should be critically questioned as to whether they really belong in the guide.

ESLint flat config makes it easy to place project specific rules right next to the style guide and version both together. A comment in the configuration that links back to the corresponding section in the style guide closes the gap between rule and rationale that pure ESLint configs often lack.


// eslint.config.ts — rules cross-reference the style guide sections
import tseslint from "typescript-eslint";

export default tseslint.config({
  rules: {
    // Style Guide §5.1: prefer interface for object shapes, type for unions
    "@typescript-eslint/consistent-type-definitions": ["error", "interface"],

    // Style Guide §5.3: generic type parameters use single uppercase letters
    // with a descriptive suffix, e.g. TEntity, not T1 or Generic
    "@typescript-eslint/naming-convention": [
      "error",
      { selector: "typeParameter", format: ["PascalCase"], prefix: ["T"] },
    ],

    // Style Guide §8.2: no default exports, always named exports
    "import/no-default-export": "error",

    // Style Guide §9.1: explicit any requires a linked ticket comment
    "@typescript-eslint/no-explicit-any": "error",
  },
});

4. Examples Instead of Prose: Right and Wrong Side by Side

Developers read code examples faster and retain them better than long prose explanations. A TypeScript style guide that shows every rule with a short, runnable before and after example gets actually used as a reference, while pure text rules usually go unread.

It matters that the examples come from real situations in the project, not from academic toy cases. An example showing a real review problem from the team's own codebase convinces the team more than a generic textbook example that has nothing to do with their own code.


// Style Guide §6.2: readonly arrays for data that must not be mutated

// WRONG — array can be mutated anywhere it is passed
function renderTags(tags: string[]): string {
  return tags.join(", ");
}

// RIGHT — readonly signals intent and prevents accidental push/splice
function renderTagsSafely(tags: readonly string[]): string {
  return tags.join(", ");
}

// Style Guide §6.4: prefer discriminated unions over optional flags
// WRONG — invalid states like { loading: true, error: "x" } are representable
interface FetchStateWrong {
  loading?: boolean;
  error?: string;
  data?: unknown;
}

// RIGHT — only valid combinations can be constructed
type FetchState =
  | { status: "loading" }
  | { status: "error"; error: string }
  | { status: "success"; data: unknown };

5. Naming Conventions for Types, Interfaces and Generics

Inconsistent naming conventions are one of the most common sources of discussion in reviews, even though a clear style guide can eliminate them entirely. Questions like whether an interface carries an I prefix, whether generics are named IEntity or TEntity, and whether enum values are written in UPPER_CASE or PascalCase should be decided once and then enforced via ESLint.

A proven convention is to give generics a descriptive T prefix, such as TEntity instead of just T, once more than one type parameter is in play. That makes the signatures of complex functions more readable, without every single caller having to study the implementation to understand the meaning of the type parameters.


// Style Guide §5: naming conventions in one place

// Interfaces: no I-prefix, PascalCase noun
interface OrderSummary { id: string; total: number }

// Generics with more than one parameter: descriptive T-prefix, not T1/T2
function mapEntries<TKey extends string, TValue>(
  entries: readonly [TKey, TValue][],
): Record<TKey, TValue> {
  return Object.fromEntries(entries) as Record<TKey, TValue>;
}

// Enum values: PascalCase, not UPPER_CASE, matches our TypeScript-first style
enum OrderStatus {
  Draft = "draft",
  Placed = "placed",
  Shipped = "shipped",
}

// Type aliases for unions: suffix with the concept, not with "Type"
type PaymentMethod = "card" | "paypal" | "invoice"; // not PaymentMethodType

// Boolean-returning functions: is/has/can prefix, no bare adjectives
function isRefundable(order: OrderSummary): boolean {
  return order.total > 0;
}

6. Structure Rules: File Organization and Export Patterns

Besides names and type patterns, a TypeScript style guide should also define where types live in the project. A proven convention is to keep types as close as possible to the code that uses them, instead of maintaining a central types.ts file that becomes unwieldy as the project grows. Only truly shared domain types move into a dedicated shared directory.

Export patterns also deserve a clear rule. Named exports instead of default exports ease refactoring, because IDEs track renames more reliably through named exports. This rule, like many structural rules in the style guide, can be enforced directly with an ESLint rule instead of hoping for discipline in review.


// Style Guide §8: co-locate types, only shared domain types move out

// src/features/orders/order.types.ts — lives next to its feature
export interface Order { id: string; total: number }

// src/shared/types/money.ts — genuinely shared across features, moved out
export interface Money { amount: number; currency: string }

// WRONG per Style Guide §8.2 — default export makes renames harder to track
export default function formatOrder(order: Order): string {
  return `#${order.id}: ${order.total}`;
}

// RIGHT — named export, IDE refactors track this reliably
export function formatOrderSafely(order: Order): string {
  return `#${order.id}: ${order.total}`;
}

7. Actively Maintaining the Style Guide Instead of Writing It Once

A TypeScript style guide is never finished. New language features, new libraries and new lessons from production incidents change which conventions make sense. A style guide that lives in the repository next to the code and gets updated through normal pull requests stays alive, because changes go through the same review process as code changes.

A simple mechanism to enforce maintenance is a requirement to add a style guide section alongside every new ESLint rule. That way the rule set and the documentation always grow in sync, instead of one lagging behind the other.

8. Introducing an Existing Style Guide to a Team

A new TypeScript style guide should never roll out overnight with full ESLint strictness over an existing codebase. A gentler path is to configure new rules as warn instead of error first, and only raise them to error after a transition period once existing violations are cleaned up.

Equally important is a short, joint presentation of the style guide in a team meeting, instead of simply sending it as a link. A team that understands why a rule exists is more likely to accept it than if ESLint suddenly throws unexplained errors in existing pull requests.

9. Style Guide Approaches Compared

The form of a TypeScript style guide decides its success more than its content. The following overview shows which practices turn a style guide into a dead wiki page and which ones actually keep it alive.

AspectDead Style GuideLived Style GuideEffect
LocationSeparate wiki systemIn the repository, next to the codeGets updated with pull requests
FormatLong prose paragraphsShort before and after code examplesFaster to read and retain
EnforcementOnly a recommendation in reviewESLint rule per style guide sectionViolations surface automatically
ScopeFormatting and architecture mixed togetherOnly decisions Prettier cannot makeClear and focused
RolloutFull strictness overnightGradually from warn to errorAcceptance instead of frustration

10. Summary

A TypeScript style guide only works if it avoids the three central traps that turn most guides into dead wiki pages: missing automation, too much prose and too broad a scope. Teams that tie rules directly to ESLint, use examples instead of prose, and limit the guide to what Prettier does not already handle end up with a document that actually gets consulted in daily work.

Equally important is active maintenance in the repository and a gradual rollout on existing codebases. A style guide that gets versioned with the code and only adds new rules together with a rationale stays relevant for years, instead of going stale after a few months.

Creating a TypeScript style guide, the essentials at a glance

Limit the scope

Only govern decisions Prettier cannot make automatically, otherwise the guide becomes unwieldy.

Enforce automatically

Every rule needs an ESLint counterpart, otherwise it vanishes from daily practice within weeks.

Examples over prose

Short before and after code examples from real project situations instead of long text sections.

Maintain in the repository

The guide lives next to the code and grows in sync with every new ESLint rule.

11. FAQ: Creating a TypeScript Style Guide

1What does not belong in a TypeScript style guide?
Pure formatting questions like indentation or quote style belong in the Prettier configuration, not in the style guide, which should focus on type patterns and conventions.
2Does every rule in the style guide need to be ESLint enforceable?
Ideally yes. Rules without automation tend to quickly vanish from the team's lived daily practice, no matter how well documented they are.
3How do you introduce a style guide in an old codebase?
Configure new rules as warn instead of error first, and only tighten them after a transition period once existing violations are cleaned up.
4Where should a TypeScript style guide be stored?
In the repository itself, versioned next to the code, instead of in a separate wiki system that easily goes stale.
5How many examples does a single style guide rule need?
A short before and after pair usually suffices, as long as it comes from a real situation in the team's own project rather than a generic textbook case.
6Should interfaces use an I prefix?
That is purely a convention, but the current TypeScript community and the official handbook style now advise against it, because the prefix adds no extra information.
7How often should a style guide be updated?
Whenever a new ESLint rule gets introduced. This coupling prevents the rule set and the documentation from drifting apart.
8Can you adopt an existing style guide from another company?
As a starting point yes, but it should be adapted to the project and complemented with your own examples from your own codebase, otherwise it stays abstract.
9How do you convince a team that is generally skeptical of style guides?
With automated enforcement instead of appeals. If ESLint enforces the rule anyway, a review discussion turns into a technical formality.
10What if two teammates prefer different conventions?
Make a team decision, document it and enforce it via ESLint. From that point the discussion is over until a valid reason for a change comes up.