TypeScript Lesson Plan
A progressive curriculum to master TypeScript through hands-on practice.
Lesson 1: First Steps
Section titled “Lesson 1: First Steps”Goal: Set up TypeScript and understand basic types.
Concepts
Section titled “Concepts”TypeScript adds static types to JavaScript. Types catch errors at compile time, not runtime.
Exercises
Section titled “Exercises”-
Setup and run
Terminal window npm init -ynpm install -D typescriptnpx tsc --inithello.ts const greeting: string = "Hello, TypeScript!";console.log(greeting);Terminal window npx tsc hello.tsnode hello.js -
Basic types
// Primitiveslet name: string = "Alice";let age: number = 30;let active: boolean = true;// Arrayslet nums: number[] = [1, 2, 3];let names: Array<string> = ["a", "b"];// Tuplelet point: [number, number] = [10, 20];// Any (escape hatch)let anything: any = "could be anything"; -
Type inference
// TypeScript infers typeslet x = 10; // numberlet s = "hello"; // string// Function return type inferredfunction add(a: number, b: number) {return a + b; // Returns number} -
Type annotations
// Variableslet count: number;count = 42;// Functionsfunction greet(name: string): string {return `Hello, ${name}!`;}// Arrow functionsconst double = (n: number): number => n * 2;
Checkpoint
Section titled “Checkpoint”Write a function that takes a name and age and returns a formatted greeting.
Lesson 2: Objects and Interfaces
Section titled “Lesson 2: Objects and Interfaces”Goal: Define object shapes with interfaces.
Exercises
Section titled “Exercises”-
Object types
// Inline object typelet user: { name: string; age: number } = {name: "Alice",age: 30,};// Optional propertieslet config: { host: string; port?: number } = {host: "localhost",}; -
Interfaces
interface User {name: string;age: number;email?: string; // Optionalreadonly id: number; // Cannot modify}const user: User = {id: 1,name: "Alice",age: 30,}; -
Extending interfaces
interface Animal {name: string;}interface Dog extends Animal {breed: string;}const dog: Dog = {name: "Rex",breed: "Labrador",}; -
Index signatures
interface Dictionary {[key: string]: string;}const dict: Dictionary = {hello: "world",foo: "bar",};
Checkpoint
Section titled “Checkpoint”Define an interface for a blog post with title, content, author, and optional tags.
Lesson 3: Union and Literal Types
Section titled “Lesson 3: Union and Literal Types”Goal: Create flexible yet precise types.
Exercises
Section titled “Exercises”-
Union types
let id: string | number;id = "abc";id = 123;function printId(id: string | number) {if (typeof id === "string") {console.log(id.toUpperCase());} else {console.log(id);}} -
Literal types
type Direction = "north" | "south" | "east" | "west";function move(dir: Direction) {console.log(`Moving ${dir}`);}move("north"); // OK// move("up"); // Error! -
Discriminated unions
interface Circle {kind: "circle";radius: number;}interface Square {kind: "square";size: number;}type Shape = Circle | Square;function area(shape: Shape): number {switch (shape.kind) {case "circle":return Math.PI * shape.radius ** 2;case "square":return shape.size ** 2;}} -
Narrowing
function process(value: string | string[] | null) {if (value === null) {return "nothing";}if (Array.isArray(value)) {return value.join(", ");}return value.toUpperCase();}
Checkpoint
Section titled “Checkpoint”Create a type for API responses that can be success (with data) or error (with message).
Lesson 4: Functions
Section titled “Lesson 4: Functions”Goal: Type functions precisely.
Exercises
Section titled “Exercises”-
Function types
// Function typetype MathOp = (a: number, b: number) => number;const add: MathOp = (a, b) => a + b;const multiply: MathOp = (a, b) => a * b; -
Optional and default parameters
function greet(name: string, greeting: string = "Hello"): string {return `${greeting}, ${name}!`;}function log(message: string, userId?: number): void {console.log(message, userId ?? "anonymous");} -
Rest parameters
function sum(...nums: number[]): number {return nums.reduce((a, b) => a + b, 0);}sum(1, 2, 3, 4); // 10 -
Overloads
function parse(input: string): string[];function parse(input: string[]): string;function parse(input: string | string[]): string | string[] {if (typeof input === "string") {return input.split(",");}return input.join(",");}
Checkpoint
Section titled “Checkpoint”Create a typed event handler function that accepts different event types.
Lesson 5: Generics
Section titled “Lesson 5: Generics”Goal: Write reusable, type-safe code.
Exercises
Section titled “Exercises”-
Generic functions
function identity<T>(value: T): T {return value;}identity<string>("hello"); // Explicitidentity(42); // Inferred as number -
Generic interfaces
interface Box<T> {value: T;}const stringBox: Box<string> = { value: "hello" };const numberBox: Box<number> = { value: 42 };interface Result<T, E> {data?: T;error?: E;} -
Generic constraints
interface HasLength {length: number;}function logLength<T extends HasLength>(item: T): void {console.log(item.length);}logLength("hello"); // OKlogLength([1, 2, 3]); // OK// logLength(42); // Error: number has no length -
Generic with keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {return obj[key];}const user = { name: "Alice", age: 30 };getProperty(user, "name"); // stringgetProperty(user, "age"); // number// getProperty(user, "email"); // Error!
Checkpoint
Section titled “Checkpoint”Create a generic Stack<T> class with push, pop, and peek methods.
Lesson 6: Utility Types
Section titled “Lesson 6: Utility Types”Goal: Transform types with built-in utilities.
Exercises
Section titled “Exercises”-
Partial and Required
interface User {name: string;email: string;age?: number;}// All properties optionaltype PartialUser = Partial<User>;// All properties requiredtype RequiredUser = Required<User>;// Good for updatesfunction updateUser(id: number, updates: Partial<User>) {} -
Pick and Omit
interface User {id: number;name: string;email: string;password: string;}// Only specific propertiestype PublicUser = Pick<User, "id" | "name" | "email">;// All except specific propertiestype SafeUser = Omit<User, "password">; -
Record
type Status = "pending" | "active" | "completed";// Map status to countstype StatusCounts = Record<Status, number>;const counts: StatusCounts = {pending: 5,active: 10,completed: 25,}; -
Readonly and NonNullable
interface Config {host: string;port: number;}const config: Readonly<Config> = {host: "localhost",port: 3000,};// config.port = 8080; // Error!type MaybeString = string | null | undefined;type DefiniteString = NonNullable<MaybeString>; // string
Checkpoint
Section titled “Checkpoint”Use utility types to create a read-only version of an API response type.
Lesson 7: Advanced Types
Section titled “Lesson 7: Advanced Types”Goal: Master complex type patterns.
Exercises
Section titled “Exercises”-
Mapped types
type Flags<T> = {[K in keyof T]: boolean;};interface Features {darkMode: string;notifications: string;}type FeatureFlags = Flags<Features>;// { darkMode: boolean; notifications: boolean } -
Conditional types
type IsString<T> = T extends string ? true : false;type A = IsString<"hello">; // truetype B = IsString<42>; // false// Extract array element typetype Unwrap<T> = T extends Array<infer U> ? U : T;type C = Unwrap<string[]>; // string -
Template literal types
type EventName = "click" | "focus" | "blur";type Handler = `on${Capitalize<EventName>}`;// "onClick" | "onFocus" | "onBlur"type Greeting = `Hello, ${string}!`;const g: Greeting = "Hello, World!"; -
Type guards
interface Cat {meow(): void;}interface Dog {bark(): void;}function isCat(animal: Cat | Dog): animal is Cat {return (animal as Cat).meow !== undefined;}function speak(animal: Cat | Dog) {if (isCat(animal)) {animal.meow(); // TypeScript knows it's Cat} else {animal.bark(); // TypeScript knows it's Dog}}
Checkpoint
Section titled “Checkpoint”Create a type that makes all properties of a nested object optional (DeepPartial).
Lesson 8: Modules and Declaration Files
Section titled “Lesson 8: Modules and Declaration Files”Goal: Organize code and use external libraries.
Exercises
Section titled “Exercises”-
ES modules
math.ts export function add(a: number, b: number): number {return a + b;}export const PI = 3.14159;export default class Calculator {}// main.tsimport Calculator, { add, PI } from "./math";import * as math from "./math"; -
Type-only imports
import type { User } from "./types";// Only import the type, not the value// Removed at compile time -
Declaration files
types.d.ts declare module "untyped-lib" {export function doSomething(input: string): string;export const version: string;}// Ambient declarationsdeclare global {interface Window {myApp: {version: string;};}} -
tsconfig essentials
{"compilerOptions": {"target": "ES2020","module": "ESNext","strict": true,"esModuleInterop": true,"skipLibCheck": true,"outDir": "./dist","rootDir": "./src"},"include": ["src/**/*"],"exclude": ["node_modules"]}
Checkpoint
Section titled “Checkpoint”Create a module with types, export them, and import in another file.
Practice Projects
Section titled “Practice Projects”Project 1: Type-Safe API Client
Section titled “Project 1: Type-Safe API Client”Build an API client:
- Generic request/response types
- Error handling with discriminated unions
- Type-safe query parameters
Project 2: State Management
Section titled “Project 2: State Management”Build a simple store:
- Generic state type
- Type-safe actions
- Selector functions with proper return types
Project 3: CLI Tool
Section titled “Project 3: CLI Tool”Build a command-line tool:
- Use Commander.js with types
- Validate input with Zod
- Type-safe configuration
Quick Reference
Section titled “Quick Reference”| Stage | Topics |
|---|---|
| Beginner | Basic types, interfaces, functions |
| Intermediate | Generics, union types, utility types |
| Advanced | Mapped types, conditional types, declaration |
See Also
Section titled “See Also”- TypeScript Cheatsheet — Quick syntax reference
- Testing — Jest/Vitest with TypeScript