Code of the Day
AdvancedTooling & Types

Advanced TypeScript

Generics, utility types, and narrowing — types that scale with your code.

JavaScript / TSAdvanced9 min read
Recommended first
By the end of this lesson you will be able to:
  • Write reusable code with generics
  • Reshape types with utility types
  • Let TypeScript narrow unions through control flow

The TypeScript basics lesson covered annotations and . The type system goes much further — letting you write code that's both reusable and precisely typed. These are the tools that make types help rather than fight you on a large codebase.

Generics: reusable and type-safe

A is a type parameter — it lets a function or type work over any type while preserving it, rather than falling back to any:

function first<T>(items: T[]): T | undefined {
  return items[0];
}

first([1, 2, 3]);        // inferred as number
first(["a", "b"]);       // inferred as string

T is filled in per call, so first works for every element type while still knowing exactly what it returns. This is how Array, Promise<T>, and most library types stay both flexible and safe.

Utility types

TypeScript ships utility types that transform existing types, so you don't repeat yourself:

interface User { id: number; name: string; email: string; }

Partial<User>            // all fields optional
Pick<User, "id" | "name"> // just those fields
Omit<User, "email">       // everything except email
Readonly<User>            // all fields read-only

Derive related types from one source of truth instead of hand-writing each variant — the DRY principle at the type level.

Narrowing

TypeScript narrows a union type as it follows your control flow, so each branch knows the precise type:

function format(id: string | number): string {
  if (typeof id === "string") {
    return id.toUpperCase();   // here, TypeScript knows id is a string
  }
  return id.toFixed(0);        // here, it knows id is a number
}

The compiler reads your if/typeof checks and refines the type accordingly — this is what makes unions (the data-and-types idea) pleasant to work with, forcing you to handle each case.

Resist over-engineering types. Reach for advanced features when they remove real duplication or catch real bugs — not to show off. Clear, slightly-repetitive types beat a clever one nobody can read (the clarity-over-cleverness rule).

Where to go next

Last in the track: proving JavaScript and TypeScript code works — testing JavaScript.

Finished reading? Mark it complete to track your progress.

On this page