edit_document// BLOG_POST.md

TypeScript Tips & Tricks: Patterns That Separate Juniors from Seniors

//

, ,

TypeScript has become the default choice for serious JavaScript development. With over 43% of developers using it (Stack Overflow 2025), its type system catches entire categories of bugs at compile time. But TypeScript’s power goes far beyond basic annotations — its advanced type system is a programming language in its own right. Here are techniques that separate proficient developers from beginners.

Generics: Write Once, Type Everything

// Generic API response wrapper — type-safe for any data shape
interface ApiResponse<T> {
    status: number;
    data: T;
    timestamp: string;
}

function handleResponse<T>(response: ApiResponse<T>): T {
    if (response.status >= 400) throw new Error(`API error: ${response.status}`);
    return response.data;
}

interface User { id: string; name: string; email: string; }
const userResp: ApiResponse<User> = { status: 200, data: { id: '1', name: 'Alice', email: 'alice@example.com' }, timestamp: new Date().toISOString() };
const user = handleResponse(userResp);
// user is typed as User — full autocomplete, full safety

// Constrained generic — T must have an 'id' property
function findById<T extends { id: string }>(items: T[], id: string): T | undefined {
    return items.find(item => item.id === id);
}

Discriminated Unions

By adding a literal type field, you get exhaustive pattern matching that eliminates impossible states:

type FetchState<T> =
    | { type: 'idle' }
    | { type: 'loading'; startedAt: number }
    | { type: 'success'; data: T; fetchedAt: number }
    | { type: 'error'; message: string; retryCount: number };

function renderState<T>(state: FetchState<T>): string {
    switch (state.type) {
        case 'idle':    return 'Ready';
        case 'loading': return `Loading... (${Date.now() - state.startedAt}ms)`;
        case 'success': return `Got: ${JSON.stringify(state.data)}`;
        case 'error':   return `Error: ${state.message} (retry ${state.retryCount}/3)`;
        // Remove a case → compile error. Impossible to forget a state.
    }
}

Utility Types You Should Know

interface User { id: string; name: string; email: string; role: 'admin'|'editor'|'viewer'; createdAt: Date; }

type UserUpdate = Partial<Omit<User, 'id' | 'createdAt'>>;  // All fields optional except id/createdAt
type UserSummary = Pick<User, 'id' | 'name' | 'role'>;       // Just id, name, role
type UserMap = Record<string, User>;                          // Typed lookup table
type NotAdmin = Exclude<User['role'], 'admin'>;               // 'editor' | 'viewer'

// ReturnType extracts a function's return type
function createUser(name: string) { return { id: crypto.randomUUID(), name, createdAt: new Date() }; }
type CreatedUser = ReturnType<typeof createUser>;

Template Literal Types

type Entity = 'user' | 'order' | 'product';
type Action = 'created' | 'updated' | 'deleted';
type EventName = `${Entity}:${Action}`;
// 'user:created' | 'user:updated' | ... (9 combinations, all type-safe)

The satisfies Operator

Validates that a value matches a type WITHOUT widening its inferred type — best of both worlds:

type Theme = Record<string, string | number>;
const theme = {
    primary: '#6366f1',
    fontSize: 16,
} satisfies Theme;
theme.primary;   // Type: '#6366f1' (not string!)
theme.fontSize;  // Type: 16 (not number!)

Mapped & Conditional Types

// Make all string properties nullable
type Nullable<T> = { [K in keyof T]: T[K] extends string ? T[K] | null : T[K]; };

// Deep readonly — recursively freeze nested objects
type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]; };

These patterns compound. Once you internalize generics, discriminated unions, utility types, and satisfies, you write code that’s simultaneously more flexible and more type-safe.

Further reading: TypeScript Handbook | SO 2025 Survey


arrow_circle_right// POST_NAVIGATION

forum// COMMENTS

Leave a Reply

Your email address will not be published. Required fields are marked *