TypeScript Advanced Types

Overview

TypeScript’s type system is one of the most powerful in mainstream programming languages. This note covers advanced type features.

Conditional Types

What Are Conditional Types?

Conditional types allow you to create types that depend on other types.

type IsString<T> = T extends string ? true : false;
 
type A = IsString<"hello">; // true
type B = IsString<42>;      // false

Mapped Types

Transforming Types

Mapped types let you create new types by transforming existing ones.

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

Practical Example

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}
 
type UserUpdate = Partial<User>;
type UserPreview = Pick<User, "id" | "name">;
type UserWithoutEmail = Omit<User, "email">;

Template Literal Types

String Manipulation

TypeScript can manipulate string types at the type level.

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

Discriminated Unions

Pattern Matching

Discriminated unions enable exhaustive pattern matching.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: 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;
  }
}

Utility Types

TypePurposeExample
Partial<T>All optionalPartial<User>
Required<T>All requiredRequired<Config>
Readonly<T>All readonlyReadonly<State>
Pick<T, K>Select fieldsPick<User, "id">
Omit<T, K>Exclude fieldsOmit<User, "email">
Record<K, V>Key-value mapRecord<string, number>

Branded Types

Type Safety for Primitives

Branded types prevent mixing up similar primitive types.

type UserId = string & { __brand: "UserId" };
type PostId = string & { __brand: "PostId" };
 
function createUserId(id: string): UserId {
  return id as UserId;
}
 
const userId = createUserId("123");
const postId = "456" as PostId;
 
getUser(userId);  // ✅ Works
getUser(postId);  // ❌ Compile error

See Also


*Tags: typescript javascript programming types