Skip to content
All essays
WebFebruary 7, 202514 min

TypeScript Advanced Patterns: Master Type-Level Programming

Master advanced TypeScript patterns. Learn conditional types, mapped types, template literals, and type manipulations.

Ü
Ümit Uz
Mobile & Full Stack Developer

Conditional Types

Basic Conditional Types

typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false

Nested Conditionals

typescript
type NonNullable<T> = T extends null | undefined ? never : T;

type Result = NonNullable<string | null>;  // string

Distributive Conditional Types

typescript
type ToArray<T> = T extends any ? T[] : never;

type Numbers = ToArray<number | string>;  // number[] | string[]

Mapped Types

Basic Mapped Types

typescript
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Optional<T> = {
  [P in keyof T]?: T[P];
};

type User = {
  name: string;
  age: number;
};

type ReadonlyUser = Readonly<User>;
type PartialUser = Optional<User>;

Key Remapping

typescript
type Getters<T> = {
  [P in keyof T as `get${Capitalize<P & string}}]: () => T[P];
};

type UserGetters = Getters<User>;
// {
//   getName: () => string;
//   getAge: () => number;
// }

Template Literal Types

String Manipulation

typescript
type EventName<T extends string> = `on${Capitalize<T>}Change`;

type ClickEvent = EventName<'click'>;  // "onClickChange"

type Combine<K, P> = K extends string ? P extends string ? `${K}.${P}` : never : never;
type Combined = Combine<'user', 'name'>;  // "user.name"

String Templates

typescript
type AllKeys<T> = T extends any ? T : never;

type Color = 'red' | 'blue' | 'green';
type Size = 'small' | 'medium' | 'large';

type ColorSize = `${Color}-${Size}`;
// "red-small" | "red-medium" | ... | "green-large"

Utility Types

Built-in Utility Types

typescript
// Partial - Make all properties optional
type PartialUser = Partial<User>;

// Required - Make all properties required
type RequiredUser = Required<Partial<User>>;

// Readonly - Make all properties readonly
type ReadonlyUser = Readonly<User>;

// Pick - Select specific properties
type NameOnly = Pick<User, 'name'>;

// Omit - Remove specific properties
type AgeOnly = Omit<User, 'name' | 'email'>;

// Record - Create object type
type UserMap = Record<string, User>;

// Exclude - Remove from union
type Numbers = Exclude<string | number, string>;

// Extract - Keep from union
type OnlyStrings = Extract<string | number, string>;

// ReturnType - Get return type
type Func = () => string;
type Return = ReturnType<Func>;  // string

Custom Utility Types

typescript
// DeepPartial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// DeepRequired
type DeepRequired<T> = {
  [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};

// DeepReadonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// Nullable
type Nullable<T> = T | null;

// Maybe
type Maybe<T> = T | null | undefined;

Type Guards

Type Guard Functions

typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isNumber(value: unknown): value is number {
  return typeof value === 'number';
}

function process(value: unknown) {
  if (isString(value)) {
    // TypeScript knows value is string
    console.log(value.toUpperCase());
  }
}

Discriminated Unions

typescript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number }
  | { kind: 'triangle'; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'rectangle':
      return shape.width * shape.height;
    case 'triangle':
      return 0.5 * shape.base * shape.height;
  }
}

Branded Types

typescript
type USD = number & { readonly __brand: 'USD' };
type EUR = number & { readonly __brand: 'EUR' };

const usd = (amount: number): USD => amount as USD;
const eur = (amount: number): EUR => amount as EUR;

const addFunds = (a: USD, b: USD): USD => {
  return usd((a + b) as number);
};

// Type-safe - can't mix currencies
const total = addFunds(usd(100), usd(50));
// addFunds(usd(100), eur(50)) // Error!

Type Inference

Infer Keyword

typescript
type Unpromise<T> = T extends Promise<infer U> ? U : T;

type Result = Unpromise<Promise<string>>;  // string

type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type Head = First<[1, 2, 3]>;  // 1

ReturnType Enhancement

typescript
type AsyncReturnType<T extends (...args: any) => Promise<any>> =
  T extends (...args: any) => Promise<infer R> ? R : never;

async function getData(): Promise<{ id: number }> {
  return { id: 1 };
}

type Data = AsyncReturnType<typeof getData>;  // { id: number }

Advanced Patterns

Function Overloading

typescript
function combine(a: string, b: string): string;
function combine(a: number, b: number): number;
function combine(a: any, b: any): any {
  return a + b;
}

const result1 = combine('hello', 'world');  // string
const result2 = combine(1, 2);  // number

Generic Constraints

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: 'John', age: 30 };
const name = getProperty(user, 'name');  // string
const age = getProperty(user, 'age');  // number

Conditional Inference

typescript
type Unwrap<T> =
  T extends Array<infer U> ? U :
  T extends Promise<infer U> ? U :
  T;

type Unwrapped = Unwrap<string[]>;  // string
type UnwrappedPromise = Unwrap<Promise<number>>;  // number

Best Practices

  1. 1Prefer type inference: Let TypeScript infer when possible
  2. 2Use strict mode: Catch errors at compile time
  3. 3Avoid any: Use unknown instead
  4. 4Use utility types: Leverage built-in types
  5. 5Brand primitives: Add type safety to primitives
  6. 6Type guards: Narrow types properly
  7. 7Conditional types: For flexible type logic

Conclusion

Advanced TypeScript patterns enable type-safe, expressive code. Master these patterns to write robust applications.

Related essays

Next essay
OWASP Top 10 Security Vulnerabilities and Prevention