TypeScript Advanced Guide: Types, Generics, and Patterns
Master advanced TypeScript concepts. Learn generics, utility types, conditional types, mapped types, and professional patterns for type-safe code.
Moshiour Rahman
Advertisement
Why Advanced TypeScript?
TypeScript’s type system is incredibly powerful. Mastering advanced features enables better code organization, fewer bugs, and improved developer experience.
Setup
npm install -g typescript
tsc --init
Advanced Types
Union and Intersection Types
// Union types (OR)
type Status = "pending" | "approved" | "rejected";
type ID = string | number;
function printId(id: ID) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id);
}
}
// Intersection types (AND)
type Person = { name: string; age: number };
type Employee = { employeeId: string; department: string };
type Staff = Person & Employee;
const staff: Staff = {
name: "John",
age: 30,
employeeId: "E123",
department: "Engineering"
};
Literal Types
// String literals
type Direction = "north" | "south" | "east" | "west";
// Numeric literals
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
// Template literal types
type EventName = `on${Capitalize<string>}`;
type CSSProperty = `${string}-${string}`;
// Const assertions
const config = {
endpoint: "/api",
method: "GET"
} as const;
// type: { readonly endpoint: "/api"; readonly method: "GET" }
Type Guards
// typeof guard
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return " ".repeat(padding) + value;
}
return padding + value;
}
// instanceof guard
class Dog { bark() {} }
class Cat { meow() {} }
function speak(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
// Custom type guard
interface Fish { swim(): void }
interface Bird { fly(): void }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// Discriminated unions
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
}
}
Generics
Basic Generics
// Generic function
function identity<T>(arg: T): T {
return arg;
}
const num = identity<number>(42);
const str = identity("hello"); // Type inferred
// Generic interface
interface Box<T> {
value: T;
}
const numberBox: Box<number> = { value: 42 };
const stringBox: Box<string> = { value: "hello" };
// Generic class
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const stack = new Stack<number>();
stack.push(1);
stack.push(2);
Generic Constraints
// Constrain to types with length property
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // OK
logLength([1, 2, 3]); // OK
// logLength(123); // Error: number has no length
// Using keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "John", age: 30 };
const name = getProperty(person, "name"); // string
// getProperty(person, "invalid"); // Error
Multiple Type Parameters
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
const numbers = [1, 2, 3];
const strings = map(numbers, n => n.toString());
// Generic with defaults
interface Response<T = any> {
data: T;
status: number;
}
const response: Response = { data: "anything", status: 200 };
const typedResponse: Response<User> = { data: user, status: 200 };
Utility Types
Built-in Utility Types
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial - all properties optional
type PartialUser = Partial<User>;
// Required - all properties required
type RequiredUser = Required<User>;
// Readonly - all properties readonly
type ReadonlyUser = Readonly<User>;
// Pick - select specific properties
type UserCredentials = Pick<User, "email" | "name">;
// Omit - exclude specific properties
type UserWithoutId = Omit<User, "id">;
// Record - create object type
type UserRoles = Record<string, User>;
// Exclude - remove from union
type Status = "pending" | "approved" | "rejected";
type ActiveStatus = Exclude<Status, "rejected">;
// Extract - keep from union
type PendingStatus = Extract<Status, "pending">;
// NonNullable - remove null and undefined
type Name = string | null | undefined;
type NonNullName = NonNullable<Name>; // string
// ReturnType - get function return type
function getUser() {
return { id: 1, name: "John" };
}
type UserType = ReturnType<typeof getUser>;
// Parameters - get function parameters
type GetUserParams = Parameters<typeof getUser>;
Custom Utility Types
// Make specific properties optional
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type UserWithOptionalEmail = PartialBy<User, "email">;
// Make specific properties required
type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
// Deep partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Nullable type
type Nullable<T> = T | null;
// Mutable (remove readonly)
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
Conditional Types
Basic Conditional Types
// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Extract array element type
type ArrayElement<T> = T extends (infer E)[] ? E : T;
type Elem = ArrayElement<string[]>; // string
type NotArray = ArrayElement<number>; // number
// Get promise value type
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseValue = Awaited<Promise<string>>; // string
Distributive Conditional Types
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>;
// string[] | number[]
// Prevent distribution with tuple
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type SingleArray = ToArrayNonDist<string | number>;
// (string | number)[]
Mapped Types
Basic Mapped Types
// Make all properties optional
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
// Make all properties required
type MyRequired<T> = {
[P in keyof T]-?: T[P];
};
// Make all properties readonly
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
// Transform property names
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }
Template Literal Types
type EventName<T extends string> = `${T}Changed`;
type UserEvents = EventName<"name" | "age">;
// "nameChanged" | "ageChanged"
// Create event handlers
type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void;
};
interface Config {
theme: string;
language: string;
}
type ConfigHandlers = EventHandlers<Config>;
// {
// onThemeChange: (value: string) => void;
// onLanguageChange: (value: string) => void;
// }
Advanced Patterns
Builder Pattern
class QueryBuilder<T extends object = {}> {
private query: T;
constructor(query: T = {} as T) {
this.query = query;
}
select<K extends string>(field: K): QueryBuilder<T & { select: K }> {
return new QueryBuilder({ ...this.query, select: field });
}
where<K extends string, V>(
field: K,
value: V
): QueryBuilder<T & { where: { field: K; value: V } }> {
return new QueryBuilder({
...this.query,
where: { field, value }
});
}
build(): T {
return this.query;
}
}
const query = new QueryBuilder()
.select("name")
.where("age", 30)
.build();
Factory Pattern
interface Product {
name: string;
price: number;
}
interface ProductFactory<T extends Product> {
create(data: Omit<T, "id">): T & { id: string };
}
function createFactory<T extends Product>(): ProductFactory<T> {
return {
create(data) {
return {
...data,
id: Math.random().toString(36).substr(2, 9)
} as T & { id: string };
}
};
}
interface Book extends Product {
author: string;
}
const bookFactory = createFactory<Book>();
const book = bookFactory.create({
name: "TypeScript Guide",
price: 29.99,
author: "John Doe"
});
Type-Safe Event Emitter
type EventMap = {
userLogin: { userId: string; timestamp: Date };
userLogout: { userId: string };
error: { message: string; code: number };
};
class TypedEventEmitter<T extends Record<string, any>> {
private listeners: { [K in keyof T]?: ((data: T[K]) => void)[] } = {};
on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(callback);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.listeners[event]?.forEach(callback => callback(data));
}
}
const emitter = new TypedEventEmitter<EventMap>();
emitter.on("userLogin", (data) => {
console.log(data.userId, data.timestamp);
});
emitter.emit("userLogin", {
userId: "123",
timestamp: new Date()
});
Summary
| Concept | Use Case |
|---|---|
| Generics | Reusable type-safe functions/classes |
| Utility Types | Transform existing types |
| Conditional Types | Type logic and inference |
| Mapped Types | Transform object types |
| Template Literals | String manipulation at type level |
Advanced TypeScript enables building robust, type-safe applications with excellent developer experience.
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
Next.js 14 Tutorial: Complete Guide with App Router
Master Next.js 14 with App Router. Learn server components, data fetching, routing, server actions, and build full-stack React applications.
JavaScriptReact Hooks Complete Guide: useState to Custom Hooks
Master all React hooks from basics to advanced. Learn useState, useEffect, useContext, useReducer, useMemo, useCallback, and create custom hooks.
JavaScriptBun: The Fast JavaScript Runtime and Toolkit
Master Bun for JavaScript development. Learn the runtime, package manager, bundler, test runner, and build faster applications with Bun.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.