TheThunderclap
TypeScript JavaScript

TypeScript: Advanced Type Magic

Conditional types, mapped types, template literals and type-level programming for bulletproof codebases.

R

Rahul Mehta

Frontend Architect

📅 8 February 2025
⏱ 11 min read

Introduction

TypeScript's type system is far more powerful than most developers use. Beyond basic interfaces and generics, it supports full type-level computation — you can write programs that run at compile time to validate your runtime contracts.

Conditional Types

Conditional types follow the pattern T extends U ? X : Y. They let you write types that branch based on other types, enabling powerful inference utilities.

conditional.ts
typescript
                                            "hl-keyword">class="hl-comment">// Extract the resolved "hl-keyword">type "hl-keyword">from a "hl-type">Promise
"hl-keyword">type Awaited<T> = T "hl-keyword">extends "hl-type">Promise<"hl-keyword">infer R> ? R : T;

"hl-keyword">type A = Awaited<"hl-type">Promise<"hl-type">string>>;    "hl-keyword">class="hl-comment">// "hl-type">string
"hl-keyword">type B = Awaited<"hl-type">number>;             "hl-keyword">class="hl-comment">// "hl-type">number

"hl-keyword">class="hl-comment">// Extract "hl-keyword">function "hl-keyword">return "hl-keyword">type
"hl-keyword">type ReturnType<T> = T "hl-keyword">extends (...args: "hl-type">any[]) => "hl-keyword">infer R ? R : "hl-keyword">never;

"hl-keyword">async "hl-keyword">function fetchUser() {
  "hl-keyword">return { id: '1', name: 'Anant' };
}
"hl-keyword">type User = Awaited<ReturnType<"hl-keyword">typeof fetchUser>>;
"hl-keyword">class="hl-comment">// User = { id: "hl-type">string; name: "hl-type">string }

"hl-keyword">class="hl-comment">// Distributive conditional types
"hl-keyword">type NonNullable<T> = T "hl-keyword">extends "hl-type">null | "hl-type">undefined ? "hl-keyword">never : T;
"hl-keyword">type Safe = NonNullable<"hl-type">string | "hl-type">null | "hl-type">undefined>;  "hl-keyword">class="hl-comment">// "hl-type">string
                                        

Mapped Types

Mapped types iterate over the keys of another type to produce a new shape. They are the foundation of utility types like Partial, Required, and Readonly.

mapped.ts
typescript
                                            "hl-keyword">class="hl-comment">// Build Partial<T> "hl-keyword">from scratch
"hl-keyword">type MyPartial<T> = {
  [K "hl-keyword">in "hl-keyword">keyof T]?: T[K];
};

"hl-keyword">class="hl-comment">// Make specific keys optional, rest required
"hl-keyword">type PartialBy<T, K "hl-keyword">extends "hl-keyword">keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

"hl-keyword">interface User {
  id: "hl-type">string;
  name: "hl-type">string;
  email: "hl-type">string;
  bio: "hl-type">string;
}

"hl-keyword">class="hl-comment">// bio is optional, everything "hl-keyword">else required
"hl-keyword">type UpdateUser = PartialBy<User, 'bio'>;

"hl-keyword">class="hl-comment">// Deep "hl-keyword">readonly — recursively make all nested properties "hl-keyword">readonly
"hl-keyword">type DeepReadonly<T> = {
  "hl-keyword">readonly [K "hl-keyword">in "hl-keyword">keyof T]: T[K] "hl-keyword">extends "hl-type">object ? DeepReadonly<T[K]> : T[K];
};

"hl-keyword">type Config = DeepReadonly<{
  db: { host: "hl-type">string; port: "hl-type">number };
  features: { darkMode: "hl-type">boolean };
}>;
                                        

Template Literal Types

TypeScript 4.1 introduced template literal types, letting you construct string union types programmatically. This is incredibly powerful for event systems, CSS properties, and route generation.

template-literals.ts
typescript
                                            "hl-keyword">class="hl-comment">// Generate all CSS dimension properties
"hl-keyword">type Side = 'top' | 'right' | 'bottom' | 'left';
"hl-keyword">type CSSProperty = `margin-${Side}` | `padding-${Side}` | `border-${Side}-width`;
"hl-keyword">class="hl-comment">// "margin-top" | "margin-right" | ... | "border-left-width"

"hl-keyword">class="hl-comment">// Typed event emitter
"hl-keyword">type EventMap = {
  'user:created': { id: "hl-type">string; name: "hl-type">string };
  'user:deleted': { id: "hl-type">string };
  'post:published': { postId: "hl-type">string; authorId: "hl-type">string };
};

"hl-keyword">type EventName = "hl-keyword">keyof EventMap;  "hl-keyword">class="hl-comment">// 'user:created' | 'user:deleted' | 'post:published'

"hl-keyword">function emit<E "hl-keyword">extends EventName>(event: E, payload: EventMap[E]): "hl-type">void {
  "hl-keyword">class="hl-comment">// fully "hl-keyword">type-safe!
}

emit('user:created', { id: '1', name: 'Anant' });  "hl-keyword">class="hl-comment">// ✅
"hl-keyword">class="hl-comment">// emit('user:created', { wrong: true });           // ❌ compile error
                                        

Discriminated Unions & Exhaustive Checks

Discriminated unions are the most ergonomic way to model "one of several shapes" in TypeScript. Pair them with exhaustive checks to catch unhandled cases at compile time.

discriminated.ts
typescript
                                            "hl-keyword">type ApiResult<T> =
  | { status: 'success'; data: T }
  | { status: 'error';   error: "hl-type">string; code: "hl-type">number }
  | { status: 'loading' };

"hl-keyword">class="hl-comment">// Exhaustive "hl-keyword">switch — TypeScript will error "hl-keyword">if you add a "hl-keyword">new status and forget to handle it
"hl-keyword">function renderResult<T>(result: ApiResult<T>): "hl-type">string {
  "hl-keyword">switch (result.status) {
    "hl-keyword">case 'success': "hl-keyword">return `Data: ${JSON.stringify(result.data)}`;
    "hl-keyword">case 'error':   "hl-keyword">return `Error ${result.code}: ${result.error}`;
    "hl-keyword">case 'loading': "hl-keyword">return 'Loading…';
    "hl-keyword">default:
      "hl-keyword">class="hl-comment">// This line makes the check exhaustive
      "hl-keyword">const _exhaustive: "hl-keyword">never = result;
      "hl-keyword">throw "hl-keyword">new Error(`Unhandled status: ${_exhaustive}`);
  }
}
                                        

💬 Comments

0 comments

Leave a comment

0/1000

Comments are moderated. Be respectful. ✌️

📚 Related Articles