Published Mar 21, 2026

TypeScript tips that will level up your codebase fast

TypeScript is much more than adding : string to your variables. The type system is incredibly powerful, and learning a few advanced patterns can dramatically improve your code quality and developer experience.

Discriminated unions for state management

Instead of optional fields and boolean flags, model your state as a discriminated union:

// Instead of this:
type RequestState = {
  loading: boolean;
  error?: string;
  data?: User[];
};

// Do this:
type RequestState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'error'; error: string }
  | { status: 'success'; data: User[] };

Now TypeScript will enforce that you handle every state, and you can never accidentally access data when the request is still loading.

Template literal types

Template literal types let you create string patterns at the type level:

type EventName = `on${Capitalize<string>}`;
type CSSUnit = `${number}${'px' | 'rem' | 'em' | '%'}`;

function setWidth(width: CSSUnit) { /* ... */ }
setWidth('100px');  // OK
setWidth('2rem');   // OK
setWidth('hello');  // Error!

The satisfies operator

The satisfies operator checks that a value matches a type without widening it:

const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} satisfies Record<string, string | number[]>;

// palette.green is still typed as string, not string | number[]
palette.green.toUpperCase(); // Works!

Utility types you should know

  • Partial<T> — makes all properties optional
  • Required<T> — makes all properties required
  • Pick<T, K> — selects specific properties
  • Omit<T, K> — removes specific properties
  • Extract<T, U> — extracts members assignable to U
  • ReturnType<T> — gets the return type of a function

These built-in utilities eliminate a huge amount of boilerplate type definitions. Combine them to build exactly the type you need.