HeoLab
ToolsBlogAboutContact
HeoLab

Free developer tools with AI enhancement. Built for developers who ship.

Tools

  • JSON Formatter
  • JWT Decoder
  • Base64 Encoder
  • Timestamp Converter
  • Regex Tester
  • All Tools →

Resources

  • Blog
  • What is JSON?
  • JWT Deep Dive
  • Base64 Explained

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 HeoLab. All rights reserved.

Tools work in your browser. Zero data retention.

HomeBlog10 TypeScript Patterns That Make Your Code Production-Ready
Table of Contents▾
  • 1. Discriminated Unions Over Optional Properties
  • 2. The `satisfies` Operator
  • 3. Template Literal Types
  • 4. Infer in Conditional Types
  • 5. Mapped Types with Modifiers
  • 6. Const Assertions and `as const`
  • 7. Function Overloads
  • 8. Generic Constraints and Default Types
  • 9. Branded/Nominal Types
  • 10. Generate Types from JSON
  • Conclusion
tutorials#typescript#javascript#patterns

10 TypeScript Patterns That Make Your Code Production-Ready

Advanced TypeScript techniques used by senior engineers — discriminated unions, satisfies, template literal types, and more.

Trong Ngo
February 22, 2026
5 min read

1. Discriminated Unions Over Optional Properties

Instead of boolean flags and optional fields, use discriminated unions to make impossible states unrepresentable:

// Bad — what is state when isLoading=false and error is undefined?
interface State {
  isLoading: boolean;
  data?: User;
  error?: Error;
}

// Good — every state is explicit
type State =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: User }
  | { status: 'error'; error: Error };

// TypeScript narrows correctly:
if (state.status === 'success') {
  console.log(state.data.name); // data is guaranteed here
}

2. The satisfies Operator

Added in TypeScript 4.9, satisfies validates a value against a type while preserving the most specific literal type:

type Color = 'red' | 'green' | 'blue';
type Config = Record<string, Color>;

// Without satisfies — loses specific keys
const theme: Config = { primary: 'red', secondary: 'blue' };
// theme.primary is typed as Color, not 'red'

// With satisfies — validates AND preserves literals
const theme = {
  primary: 'red',
  secondary: 'blue',
} satisfies Config;
// theme.primary is typed as 'red' (more specific)

3. Template Literal Types

Build precise string types from combinations:

type Direction = 'top' | 'bottom' | 'left' | 'right';
type Spacing = 'sm' | 'md' | 'lg';

type SpacingClass = `p${Capitalize<Direction>}-${Spacing}`;
// 'pTop-sm' | 'pTop-md' | 'pBottom-sm' | ...

// Event handler naming
type EventName = 'click' | 'focus' | 'blur';
type HandlerName = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'

// API endpoint typing
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${string}`;

4. Infer in Conditional Types

Extract types from complex shapes:

// Get the return type of a Promise
type Awaited<T> = T extends Promise<infer R> ? R : T;

// Get the element type of an array
type ElementOf<T> = T extends (infer E)[] ? E : never;

// Get the first argument type of a function
type FirstArg<T extends (...args: any) => any> =
  T extends (first: infer A, ...rest: any) => any ? A : never;

type UserId = FirstArg<typeof getUser>; // string

5. Mapped Types with Modifiers

// Make all properties optional
type Partial<T> = { [K in keyof T]?: T[K] };

// Make all properties required (remove ?)
type Required<T> = { [K in keyof T]-?: T[K] };

// Make all properties readonly
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// Deep partial (recursive)
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

6. Const Assertions and as const

// Without as const — type is string[]
const colors = ['red', 'green', 'blue'];

// With as const — type is readonly ['red', 'green', 'blue']
const colors = ['red', 'green', 'blue'] as const;
type Color = typeof colors[number]; // 'red' | 'green' | 'blue'

// Object as const
const ROUTES = {
  home: '/',
  tools: '/tools',
  blog: '/blog',
} as const;
type Route = typeof ROUTES[keyof typeof ROUTES]; // '/' | '/tools' | '/blog'

7. Function Overloads

// Different input types → different output types
function parseId(id: string): number;
function parseId(id: number): string;
function parseId(id: string | number): string | number {
  return typeof id === 'string' ? parseInt(id) : id.toString();
}

const num = parseId('123');   // typed as number
const str = parseId(123);    // typed as string

8. Generic Constraints and Default Types

// Constraint — T must have a name property
function getName<T extends { name: string }>(item: T): string {
  return item.name;
}

// Default generic type
interface Repository<T = User> {
  findById(id: string): Promise<T>;
  save(item: T): Promise<void>;
}

// Usage without specifying generic (uses User default)
const repo: Repository = createRepo();

9. Branded/Nominal Types

Prevent mixing up values of the same primitive type:

type UserId = string & { readonly _brand: 'UserId' };
type ProductId = string & { readonly _brand: 'ProductId' };

function createUserId(id: string): UserId {
  return id as UserId;
}

function getUser(id: UserId): User { /* ... */ }

const userId = createUserId('u_123');
const productId = 'p_456' as ProductId;

getUser(userId);     // ✓ OK
getUser(productId);  // ✗ Type error — ProductId ≠ UserId
getUser('raw-string'); // ✗ Type error

10. Generate Types from JSON

When working with API responses, instead of writing types manually, paste the JSON response into HeoLab's JSON → TypeScript converter to get accurate interfaces instantly. Then refine with the patterns above.

// Generated from API response JSON:
interface ApiResponse {
  user: {
    id: string;
    name: string;
    email: string;
    roles: string[];
  };
  token: string;
}

Conclusion

These patterns separate good TypeScript from great TypeScript. Start with discriminated unions and as const — they have the highest impact to effort ratio. Add satisfies for configuration objects and template literal types when you need precise string contracts.

Try These Tools

JSON → TypeScript Interface

Paste JSON and instantly get a typed TypeScript interface. Handles nested objects and arrays.

Related Articles

QR Codes for Developers: How They Work and How to Generate Them

4 min read

URL Slugs and SEO: Best Practices for Developers

4 min read

Bitwise Operations in JavaScript: A Practical Guide

4 min read

Back to Blog

Table of Contents

  • 1. Discriminated Unions Over Optional Properties
  • 2. The `satisfies` Operator
  • 3. Template Literal Types
  • 4. Infer in Conditional Types
  • 5. Mapped Types with Modifiers
  • 6. Const Assertions and `as const`
  • 7. Function Overloads
  • 8. Generic Constraints and Default Types
  • 9. Branded/Nominal Types
  • 10. Generate Types from JSON
  • Conclusion

Related Articles

QR Codes for Developers: How They Work and How to Generate Them

4 min read

URL Slugs and SEO: Best Practices for Developers

4 min read

Bitwise Operations in JavaScript: A Practical Guide

4 min read