Skip to content

TypeScript Style Guide

This guide specifically addresses TypeScript development, focusing on idiomatic practices, patterns, and TypeScript-specific considerations.

Foundational Code Standards provide the foundation, this guide extends them for TypeScript.

Formatting

The formatting rules for TypeScript adhere to our foundational formatting standards:

  • Consistent Indentation: Use 2 spaces for indentation.
  • Line Length: Aim for 100 characters, but allow flexibility for readability.
  • Whitespace: Use spaces around operators, after colons and semicolons, and before open braces for clarity.
  • Brace Style: Use 1TBS (the one true brace style) where the opening brace is on the same line.
  • Semicolons: Use semicolons at the end of statements for clarity.

Remember, these are guidelines; adapt them for your project's needs while keeping readability in focus.

Naming Conventions

The naming conventions for TypeScript adhere to our foundational naming conventions with no exceptions.

  • PascalCase for classes, interfaces, enums (definitions).
  • camelCase for functions, variables, properties.
    • Prefix booleans with is or has for clarity.
  • UPPER_SNAKE_CASE for constants.
  • lowercase package names, concatenated words (avoid underscores).

Additional Tips:

  • Interfaces: Use PascalCase without an I prefix.
  • Types: Use PascalCase for type aliases and enum types.
  • Members: Public members should not have an underscore prefix.

Documentation and Comments

Refer to the Foundational Code Standards for general commenting and documentation guidelines. In TypeScript, use TSDoc for documenting code. Document all public APIs clearly with examples where applicable.

Idioms and Best Practices

Use TypeScript’s Type System Effectively

  • Leverage Advanced Types: TypeScript offers powerful features like generics, enums, union types, and intersection types. Use these to create precise and flexible type definitions.
type Action =
    | { type: 'FEED_GOAT'; payload: { goatId: string; food: string } }
    | { type: 'PET_GOAT'; payload: { goatId: string } };

function handleGoatAction(action: Action) {
    // Implementation
}

Avoid Using any Type

  • Specify Types Explicitly: Minimize the use of the any type to maintain the benefits of TypeScript's static typing. Use unknown if the type is genuinely uncertain and validate it.

Good
function safelyParse(jsonString: string): unknown {
  return JSON.parse(jsonString);
}
Avoid
function parse(jsonString: string): any {
  return JSON.parse(jsonString);
}

Enums and Union Types for State Management

  • Prefer Union Types Over Enums for Simplicity: While enums can be useful, consider using union types for states or categories to reduce complexity and enhance tree-shaking.

Good
type GoatMood = 'happy' | 'hungry' | 'sleepy';
Less preferred for simple cases
enum GoatMood {
    Happy,
    Hungry,
    Sleepy,
}

Embrace Null Safety

  • Optional Chaining and Nullish Coalescing: Use optional chaining (?.) and nullish coalescing (??) operators to write cleaner, safer code that elegantly handles null and undefined values.

Good
const goatName = goat?.name ?? 'Unknown Goat';
Avoid
const goatName = goat && goat.name ? goat.name : 'Unknown Goat';

Modular Code Organization

  • Organize Code with ES Modules: Structure your project using modules to encapsulate functionality and promote reuse. Import and export types, interfaces, functions, and classes to keep the codebase organized and maintainable.
// In goat.ts
export class Goat {
  constructor(public name: string) {}
}

// In main.ts
import { Goat } from './goat';

Use Decorators for Cross-Cutting Concerns

  • Apply Decorators Wisely: TypeScript's experimental decorator feature can be used to annotate and modify classes and properties elegantly, especially in frameworks like Angular.
@Component({
  selector: 'goat-list',
  templateUrl: './goat-list.component.html',
})
export class GoatListComponent {
  // Component logic
}

Functional Programming Techniques

  • Immutability and Pure Functions: Adopt functional programming principles where appropriate. Use immutable data patterns and write pure functions to improve predictability and facilitate testing.

Good
function feedGoat(goat: Goat, food: string): Goat {
  return { ...goat, lastFedWith: food };
}
Avoid
function feedGoat(goat: Goat, food: string): void {
  goat.lastFedWith = food;
}

Prefer Interfaces over Types for Object Shapes

Use interfaces for defining object shapes due to their extendability.

interface Goat {
  name: string;
  age: number;
}

Async/Await over Promises

Use async/await for asynchronous code for better readability and error handling.

Modularize Code

Organize code into modules and use explicit import and export statements.

Tools and Resources

Ensuring a consistent development environment and utilizing static analysis tools are crucial steps for maintaining high code quality in TypeScript projects.

Static analysis tools help identify potential issues early in the development process. For TypeScript, several tools are particularly effective:

  • TSLint: A linter that checks TypeScript code for readability, maintainability, and functionality errors.
  • ESLint: A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript/TypeScript.

Additional Resources

To further enhance your TypeScript development skills and knowledge, consider exploring the following resources: