A comprehensive, structured curriculum for mastering TypeScript from basics to advanced concepts.
- Basic Types & Type Annotations
- Variables & Declarations
- Functions & Function Types
- Object Types & Interfaces
- Arrays & Tuples
- Enums
- Type Assertions
- Classes & OOP
- Generics
- Union & Intersection Types
- Type Guards & Type Narrowing
- Discriminated Unions
- Utility Types
- Type Aliases
- Literal Types
- Conditional Types
- Mapped Types
- Template Literal Types
- Index Types & keyof
- Type Inference
- Type Compatibility
- Variance
- Decorators
- satisfies Operator
- const Type Parameters
- Type Narrowing with in
- Template String Pattern Index Signatures
- Type Predicates
- Assertion Functions
- TypeScript with React
- State Management
- API Integration
- Error Handling
- Module Systems
- Declaration Files
- Configuration
- SOLID Principles
- Type-Safe Error Handling
- Immutability
- Testing with TypeScript
- Performance Optimization
- Code Organization
- ESLint & Prettier Setup
- Factory Pattern
- Builder Pattern
- Dependency Injection
- Monads & Functional Programming
- Type-Safe Event Emitters
- Generic Constraints
- Higher-Order Types
- TypeScript Compiler
- Build Tools
- Debug Tools
- VS Code Integration
- Type Definition Management
- Migration Strategies
- Start with the Foundation section
- Move through Intermediate Concepts
- Explore Advanced Type System features
- Learn Modern TypeScript Features
- Apply knowledge in Practical Applications
- Study and implement Best Practices
- Master Advanced Patterns
- Understand Tooling & Ecosystem
- Official TypeScript Handbook
- TypeScript Deep Dive
- TypeScript Weekly Newsletter
- DefinitelyTyped Repository
- TypeScript GitHub Repository
- Follow the sections in order
- Complete exercises in each module
- Build the suggested mini-projects
- Review recap questions
- Practice with real-world examples
- Contribute to open source TypeScript projects
- Basic JavaScript knowledge
- Understanding of programming fundamentals
- Code editor (VS Code recommended)
- Node.js installed
- TypeScript installed (
npm install -g typescript)
Begin with Basic Types & Type Annotations and follow the navigation links at the bottom of each page to progress through the curriculum.
In a user authentication service:
let username: string = "sai_dev";
let loginAttempts: number = 0;
let isAuthenticated: boolean = false;
// Flexible for any type but avoid overuse
let sessionData: any = {
token: "abc123",
expiresIn: 3600
};
const roles: string[] = ["admin", "user", "guest"];Use case: Strong typing prevents runtime issues when dealing with user input, session tokens, etc.
function processPayment(userId: string, amount: number, currency: string = "USD"): boolean {
// imagine calling an external payment API
console.log(`Processing ${amount} ${currency} for user ${userId}`);
return true; // success
}Optional/default params are common for currency, environment flags, or optional metadata.
interface User {
id: string;
email: string;
isVerified: boolean;
}
const getUserById = (id: string): User => {
return {
id,
email: "sai@example.com",
isVerified: true,
};
};Use case: Type safety when returning DB data or validating responses.
interface Notification {
id: string;
message: string;
seen: boolean;
}
const notifications: Notification[] = [
{ id: "1", message: "Welcome!", seen: false },
{ id: "2", message: "Update available", seen: true },
];
const unseenMessages = notifications.filter(n => !n.seen);
unseenMessages.forEach(n => {
console.log(`Unread: ${n.message}`);
});Use case: Cleanly filter and loop through typed data from a message queue or frontend API.
function logEvent(event: string | number) {
if (typeof event === "string") {
console.log("Log message:", event);
} else {
console.log("Log code:", event);
}
}Use case: Handling flexible log data passed from different microservices.
type OrderStatus = "pending" | "shipped" | "delivered" | "cancelled";
interface Order {
id: string;
status: OrderStatus;
}
const updateStatus = (order: Order, newStatus: OrderStatus) => {
order.status = newStatus;
};Use case: Enforces only valid statuses and prevents typos.
enum Role {
Admin = "ADMIN",
User = "USER",
Guest = "GUEST"
}
interface Access {
userId: string;
role: Role;
}
function hasAdminAccess(access: Access): boolean {
return access.role === Role.Admin;
}Use case: Avoid magic strings, better IDE support for roles/permissions.
interface Notifiable {
send(message: string): void;
}
class EmailService implements Notifiable {
send(message: string) {
console.log(`Sending email: ${message}`);
}
}
class SMSService extends EmailService {
override send(message: string) {
console.log(`Sending SMS: ${message}`);
}
}Use case: Create modular notification types (email, SMS, push) with shared logic.
class Repository<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
}
const userRepo = new Repository<User>();
userRepo.add({ id: "1", email: "test@example.com", isVerified: false });Use case: Reuse code safely for users, orders, logs, etc., without duplicating types.
interface UserCardProps {
name: string;
email: string;
}
const UserCard: React.FC<UserCardProps> = ({ name, email }) => (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
</div>
);Use case: Strong typing for props helps prevent bugs during component integration.
interface UserProfile {
id: number;
name: string;
email: string;
}
// Partial: make all properties optional
const updateProfile = (updates: Partial<UserProfile>) => {
// you can send only what you want to update
};
// Readonly: properties cannot be changed
const user: Readonly<UserProfile> = {
id: 1,
name: "Sai",
email: "sai@example.com"
};function isString(value: unknown): value is string {
return typeof value === "string";
}
function printValue(val: unknown) {
if (isString(val)) {
console.log(val.toUpperCase());
}
}type ApiResponse<T> = T extends string ? string : T[];
const handleResponse = <T>(data: T): ApiResponse<T> => {
if (typeof data === "string") return data;
return [data] as T[];
};Useful for extending global interfaces or third-party modules:
declare global {
interface Window {
appVersion: string;
}
}
window.appVersion = "1.0.0";// types/logger.d.ts
export interface Logger {
log: (msg: string) => void;
error: (msg: string) => void;
}
// modules/logger.ts
import type { Logger } from "./types/logger";
const consoleLogger: Logger = {
log: (msg) => console.log(msg),
error: (msg) => console.error(msg),
};type EventType = "click" | "hover";
type HandlerName = `on${Capitalize<EventType>}`; // "onClick" | "onHover"interface Config {
retries: number;
timeout: number;
}
type ConfigKeys = keyof Config; // "retries" | "timeout"const defaults = {
theme: "dark",
version: 1.0
};
type Defaults = typeof defaults; // inferredtype Result<T> = T extends string ? "string" : "non-string";type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;type LoadingState = { status: "loading" };
type ErrorState = { status: "error"; message: string };
type SuccessState = { status: "success"; data: any };
type FetchState = LoadingState | ErrorState | SuccessState;
function handleFetch(state: FetchState) {
if (state.status === "loading") return "Loading...";
if (state.status === "error") return `Error: ${state.message}`;
return `Data: ${JSON.stringify(state.data)}`;
}function assertNever(value: never): never {
throw new Error(`Unhandled case: ${value}`);
}
function handleStatus(status: "idle" | "loading" | "success") {
switch (status) {
case "idle": return "Idle";
case "loading": return "Loading";
case "success": return "Done";
default: return assertNever(status); // TS error if a case is missing
}
}interface Person {
name: string;
}
interface Employee {
id: number;
}
type Staff = Person & Employee;
const sai: Staff = { name: "Sai", id: 1001 };function format(input: string): string;
function format(input: number): string;
function format(input: string | number): string {
return `Formatted: ${input}`;
}function assertIsNumber(val: unknown): asserts val is number {
if (typeof val !== "number") {
throw new Error("Not a number");
}
}
function calculate(val: unknown) {
assertIsNumber(val);
return val * 2; // TS knows val is number here
}