TypeScript Style Guide
Writing clean and consistent TypeScript code
📐 What is a Style Guide?
A TypeScript style guide provides conventions for writing clean, readable, and maintainable code. Following consistent patterns helps teams collaborate effectively and reduces bugs in your applications.
// Good: Clear naming and structure
interface UserProfile {
firstName: string
lastName: string
email: string
}
Key Principles
Naming
Use clear, descriptive names
// Good
const userName = 'John'
// Bad
const un = 'John'
Formatting
Consistent code formatting
// Use 2 or 4 spaces
function greet() {
return 'Hello'
}
Structure
Organize code logically
// Imports first
import { User } from './types'
// Then code
Comments
Document complex logic
// Calculate tax
const tax =
price * 0.15
🔹 Naming Conventions
Follow these naming patterns for consistency:
// Variables and functions: camelCase
let userName = 'John'
function calculateTotal() { }
// Classes and Interfaces: PascalCase
class UserAccount { }
interface UserProfile { }
// Constants: UPPER_SNAKE_CASE
const MAX_RETRY_COUNT = 3
const API_BASE_URL = 'https://api.example.com'
// Private properties: prefix with underscore
class User {
private _password: string
public username: string
}
// Type aliases: PascalCase
type UserId = string | number
// Enums: PascalCase for name and members
enum UserRole {
Admin = 'ADMIN',
User = 'USER',
Guest = 'GUEST'
}
✓ Good Naming Examples:
getUserById() isAuthenticated totalAmount UserProfile
🔹 Type Annotations
When and how to use type annotations:
// ✓ Good: Explicit types for function parameters
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age} years old`
}
// ✓ Good: Type inference for simple assignments
const count = 5 // TypeScript infers number
const message = 'Hello' // TypeScript infers string
// ✓ Good: Explicit types for complex objects
const user: { name: string; age: number } = {
name: 'John',
age: 30
}
// ✗ Bad: Unnecessary type annotations
const num: number = 5 // Redundant, inference works
const str: string = 'hello' // Redundant
// ✓ Good: Use interfaces for reusable types
interface User {
name: string
age: number
}
const user: User = { name: 'John', age: 30 }
🔹 Interface vs Type
Choose the right tool for the job:
// ✓ Use interface for object shapes
interface User {
id: number
name: string
email: string
}
// ✓ Use type for unions and primitives
type Status = 'active' | 'inactive' | 'pending'
type ID = string | number
// ✓ Interface can be extended
interface Admin extends User {
role: string
permissions: string[]
}
// ✓ Type can use unions and intersections
type UserWithRole = User & { role: string }
// ✓ Use interface for public APIs
export interface ApiResponse {
data: any
status: number
message: string
}
// ✓ Use type for internal utilities
type Nullable<T> = T | null
🔹 Code Organization
Structure your TypeScript files properly:
// ✓ Good: Organized file structure
// 1. Imports (external first, then internal)
import React from 'react'
import { useState } from 'react'
import { User } from './types'
import { api } from './api'
// 2. Type definitions
interface Props {
title: string
count: number
}
// 3. Constants
const MAX_ITEMS = 100
// 4. Helper functions
function formatDate(date: Date): string {
return date.toISOString()
}
// 5. Main component/class
export default function MyComponent({ title, count }: Props) {
const [items, setItems] = useState<string[]>([])
return (
<div>
<h1>{title}</h1>
<p>Count: {count}</p>
</div>
)
}
🔹 Function Best Practices
Write clean, type-safe functions:
// ✓ Good: Explicit return types
function calculateTotal(price: number, tax: number): number {
return price + (price * tax)
}
// ✓ Good: Use arrow functions for callbacks
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map((n) => n * 2)
// ✓ Good: Optional parameters at the end
function createUser(name: string, age: number, email?: string): User {
return { name, age, email }
}
// ✓ Good: Use default parameters
function greet(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`
}
// ✓ Good: Destructure parameters
function displayUser({ name, age }: User): void {
console.log(`${name} is ${age} years old`)
}
// ✗ Bad: Too many parameters (use object instead)
function createOrder(id, name, price, quantity, discount, tax) { }
// ✓ Good: Use object parameter
interface OrderParams {
id: number
name: string
price: number
quantity: number
discount?: number
tax?: number
}
function createOrder(params: OrderParams) { }
🔹 Comments and Documentation
Document your code effectively:
// ✓ Good: JSDoc comments for functions
/**
* Calculates the total price including tax
* @param price - The base price
* @param taxRate - Tax rate as decimal (e.g., 0.15 for 15%)
* @returns The total price with tax
*/
function calculateTotal(price: number, taxRate: number): number {
return price + (price * taxRate)
}
// ✓ Good: Explain complex logic
function processData(data: string[]): number {
// Filter out empty strings and convert to numbers
const numbers = data
.filter(item => item.trim() !== '')
.map(item => parseInt(item, 10))
// Calculate sum using reduce
return numbers.reduce((sum, num) => sum + num, 0)
}
// ✗ Bad: Obvious comments
const count = 5 // Set count to 5
// ✓ Good: Explain why, not what
const TIMEOUT = 5000 // API timeout increased to handle slow connections