Code & Context

TypeScript Tips for Better Code

Practical TypeScript tips and patterns to write cleaner, safer, and more maintainable code.

SP

Saurabh Prakash

Author

Dec 15, 20254 min read
Share:

TypeScript has become the standard for building large-scale JavaScript applications. Here are some tips to level up your TypeScript skills.

1. Use Discriminated Unions

Discriminated unions make it easy to handle different cases in a type-safe way:

type Result<T> = 
  | { success: true; data: T }
  | { success: false; error: string };
 
function handleResult<T>(result: Result<T>) {
  if (result.success) {
    // TypeScript knows result.data exists here
    console.log(result.data);
  } else {
    // TypeScript knows result.error exists here
    console.error(result.error);
  }
}

2. Leverage Template Literal Types

Create precise string types with template literals:

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type APIRoute = `/api/${string}`;
type HTTPRoute = `${HTTPMethod} ${APIRoute}`;
 
// Valid: "GET /api/users"
// Invalid: "PATCH /api/users"
const route: HTTPRoute = "GET /api/users";

Template literal types are incredibly powerful for API design, CSS-in-JS, and configuration objects.

3. Use satisfies for Better Inference

The satisfies operator validates a type while preserving the narrower inferred type:

type Config = {
  theme: 'light' | 'dark';
  features: string[];
};
 
// Without satisfies - type is Config
const config1: Config = {
  theme: 'dark',
  features: ['search', 'notifications'],
};
 
// With satisfies - type is narrower
const config2 = {
  theme: 'dark',
  features: ['search', 'notifications'],
} satisfies Config;
 
// config2.theme is typed as 'dark', not 'light' | 'dark'

4. Extract Component Props

Clean way to extract and extend component props:

import { ComponentProps } from 'react';
 
// Extract props from a component
type ButtonProps = ComponentProps<'button'>;
 
// Extend with custom props
interface CustomButtonProps extends ButtonProps {
  variant: 'primary' | 'secondary';
  isLoading?: boolean;
}
 
function Button({ variant, isLoading, ...props }: CustomButtonProps) {
  return <button {...props} />;
}

5. Use const Assertions

Make objects and arrays immutable with const assertions:

// Without const assertion
const routes = ['/', '/about', '/blog'];
// type: string[]
 
// With const assertion
const routes = ['/', '/about', '/blog'] as const;
// type: readonly ['/', '/about', '/blog']
 
// Great for creating union types
type Route = typeof routes[number];
// type: '/' | '/about' | '/blog'

Pro Tip

Combine as const with object values to create exhaustive type guards.

6. Branded Types for Safety

Create distinct types for values that are structurally the same:

type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };
 
function createUserId(id: string): UserId {
  return id as UserId;
}
 
function createOrderId(id: string): OrderId {
  return id as OrderId;
}
 
function getUser(id: UserId) { /* ... */ }
function getOrder(id: OrderId) { /* ... */ }
 
const userId = createUserId('user-123');
const orderId = createOrderId('order-456');
 
getUser(userId);  // ✓ Works
getUser(orderId); // ✗ Error - can't pass OrderId to UserId

7. Utility Types You Should Know

// Partial - make all properties optional
type PartialUser = Partial<User>;
 
// Required - make all properties required
type RequiredConfig = Required<Config>;
 
// Pick - select specific properties
type UserPreview = Pick<User, 'name' | 'avatar'>;
 
// Omit - exclude specific properties
type UserWithoutPassword = Omit<User, 'password'>;
 
// Record - create an object type with keys and values
type UserRoles = Record<UserId, Role>;
 
// ReturnType - extract function return type
type ApiResponse = ReturnType<typeof fetchUser>;
 
// Parameters - extract function parameters
type FetchParams = Parameters<typeof fetchUser>;

Conclusion

These patterns will help you write more type-safe and maintainable TypeScript code. Start incorporating them into your projects and you'll see immediate benefits in code quality and developer experience.


Have a favorite TypeScript tip? Share it with me on GitHub!