What you’ll learn: What TypeScript is and why companies use it. Type annotations, interfaces, enums, and the key difference between type and interface. After this file, TypeScript will feel natural.
Assumes: You’ve read Guides 01–07 (JavaScript). TypeScript is JavaScript + types.


Section 1 β€” What Is TypeScript?

TypeScript is JavaScript with a type system. You write .ts files, and the TypeScript compiler (tsc) checks your types and compiles to .js.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  TypeScript β†’ JavaScript                         β”‚
β”‚                                                                  β”‚
β”‚  You write:                    Compiles to:                      β”‚
β”‚  ─────────────────             ─────────────────                 β”‚
β”‚  function add(              β†’  function add(                     β”‚
β”‚    a: number,               β†’    a,                              β”‚
β”‚    b: number               β†’    b                               β”‚
β”‚  ): number {                β†’  ) {                               β”‚
β”‚    return a + b;            β†’    return a + b;                   β”‚
β”‚  }                          β†’  }                                 β”‚
β”‚                                                                  β”‚
β”‚  Types are ERASED at runtime. They only exist during development. β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why TypeScript?

// Without TypeScript β€” bug found only at runtime:
function greet(user) {
    return `Hello, ${user.firstName}`;   // what if user.firstName doesn't exist?
}

greet({ name: "Alice" });   // Runtime: "Hello, undefined"  ← bug!

// With TypeScript β€” bug found at compile time:
interface User {
    firstName: string;
    lastName: string;
}

function greet(user: User): string {
    return `Hello, ${user.firstName}`;
}

greet({ name: "Alice" });
//     ^^^^^^^^^^^^^^^ Error: Object literal may only specify known properties,
//                    and 'name' does not exist in type 'User'. Did you mean 'firstName'?

Section 2 β€” Setup

# Install TypeScript
npm install typescript --save-dev

# Create tsconfig.json (TypeScript configuration)
npx tsc --init

# Compile
npx tsc            # compiles all .ts files per tsconfig.json
npx tsc --watch    # recompile on changes

# Run TypeScript directly (development only)
npm install ts-node --save-dev
npx ts-node src/index.ts

tsconfig.json β€” key settings

{
    "compilerOptions": {
        "target": "ES2022",           // which JS version to compile to
        "module": "commonjs",         // module system (commonjs for Node.js)
        "outDir": "./dist",           // where to put compiled JS files
        "rootDir": "./src",           // where your TS source files are
        "strict": true,               // enable all strict checks (DO use this)
        "esModuleInterop": true,      // fixes import compatibility issues
        "skipLibCheck": true,         // skip checking @types files
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true     // allow import of JSON files
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}

Section 3 β€” Type Annotations

Add types to variables, parameters, and return values with : syntax:

// Variable annotations
const name: string = "Alice";
const age: number = 25;
const isActive: boolean = true;
const data: null = null;
const result: undefined = undefined;

// In practice, TypeScript infers types from values:
const name = "Alice";    // TypeScript already knows this is string
const age = 25;          // TypeScript already knows this is number

// Annotate when TypeScript can't infer:
let value: string;       // no initial value β€” need annotation
value = "hello";

// Function parameters and return type
function add(a: number, b: number): number {
    return a + b;
}

// Arrow functions
const multiply = (a: number, b: number): number => a * b;

// Void return type (function returns nothing)
function logMessage(message: string): void {
    console.log(message);
}

// Optional parameter (add ?)
function greet(name: string, greeting?: string): string {
    return `${greeting ?? "Hello"}, ${name}!`;
}
greet("Alice");           // "Hello, Alice!"
greet("Alice", "Hi");    // "Hi, Alice!"

Section 4 β€” Basic Types

Primitive Types

let name: string = "Alice";
let age: number = 25;            // integers and floats β€” both are 'number'
let isActive: boolean = true;

// Explicit null and undefined (only when strict mode requires it)
let nullValue: null = null;
let undefinedValue: undefined = undefined;

any β€” escape hatch (avoid when possible)

let anything: any = "hello";   // accepts any type
anything = 42;                  // OK
anything = { name: "Alice" };   // OK
anything.doesntExist();         // TypeScript won't catch this error!

// Use 'any' only when migrating JavaScript to TypeScript,
// or when dealing with truly dynamic data (like raw JSON from an API)

unknown β€” safe alternative to any

let value: unknown = fetchData();   // we don't know what came back

// Must narrow the type before using:
if (typeof value === "string") {
    value.toUpperCase();    // OK β€” TypeScript knows it's a string here
}

// vs. any which requires no check:
let bad: any = fetchData();
bad.toUpperCase();    // No error, but might crash at runtime!

never β€” impossible value

// Function that never returns (throws or infinite loop):
function fail(message: string): never {
    throw new Error(message);   // never returns
}

// Used in exhaustive checks (covered in Guide 09)

void β€” function returns nothing

function log(message: string): void {
    console.log(message);
    // no return, or return with no value
}

Arrays

const numbers: number[] = [1, 2, 3];
const names: string[] = ["Alice", "Bob"];
const flags: boolean[] = [true, false, true];

// Generic syntax (same meaning):
const numbers2: Array<number> = [1, 2, 3];

// Array of objects:
const users: { name: string; age: number }[] = [
    { name: "Alice", age: 25 },
    { name: "Bob",   age: 30 }
];

Tuples β€” fixed-length, typed arrays

// Tuple: exactly [string, number, boolean] in that order
const user: [string, number, boolean] = ["Alice", 25, true];

const [name, age, isActive] = user;  // destructuring with types
name;       // string
age;        // number
isActive;   // boolean

// Common use: function returning multiple values
function getMinMax(arr: number[]): [number, number] {
    return [Math.min(...arr), Math.max(...arr)];
}
const [min, max] = getMinMax([3, 1, 4, 1, 5, 9]);

Section 5 β€” Union Types

A value can be one of several types:

// String OR number
let id: string | number;
id = "abc-123";   // OK
id = 42;          // OK
id = true;        // Error!

// Function accepting multiple types
function formatId(id: string | number): string {
    if (typeof id === "string") {
        return id.toUpperCase();    // TypeScript knows it's string here
    }
    return id.toString();           // TypeScript knows it's number here
}

// Nullable type (very common)
function findUser(id: number): User | null {
    // returns User or null if not found
}

// Optional property shorthand: User | undefined
function getConfig(key: string): string | undefined {
    return config[key];  // might not exist
}

Literal Types

// Only specific string values allowed:
type Direction = "north" | "south" | "east" | "west";
type Status = "pending" | "active" | "completed" | "cancelled";
type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

function moveRobot(direction: Direction): void {
    // direction can only be one of the four values
}
moveRobot("north");   // OK
moveRobot("up");      // Error!

// Mix types and literals:
type FlexibleId = string | 0;   // string or specifically the number 0

Section 6 β€” Interfaces

Interfaces describe the shape of an object:

interface User {
    id: number;
    name: string;
    email: string;
    age?: number;           // optional property (can be undefined)
    readonly createdAt: Date;  // read-only (can't be changed after creation)
}

// All required properties must be present:
const user: User = {
    id: 1,
    name: "Alice",
    email: "alice@example.com",
    createdAt: new Date()
    // age is optional β€” OK to omit
};

user.createdAt = new Date();   // Error! readonly

// Using in function:
function sendEmail(user: User): void {
    console.log(`Sending to ${user.email}`);
}

Interface with methods

interface FleetService {
    getFleet(fleetId: string): Promise<Fleet>;
    createFleet(data: CreateFleetInput): Promise<Fleet>;
    updateFleet(fleetId: string, updates: Partial<Fleet>): Promise<Fleet>;
    deleteFleet(fleetId: string): Promise<void>;
}

// Implement the interface in a class:
class FleetServiceImpl implements FleetService {
    async getFleet(fleetId: string): Promise<Fleet> {
        // must implement all methods from interface
    }
    // ... rest of methods
}

Interface extension

interface Animal {
    name: string;
    age: number;
}

interface Dog extends Animal {
    breed: string;
    isVaccinated: boolean;
}

// Dog must have: name, age, breed, isVaccinated
const dog: Dog = {
    name: "Rex",
    age: 3,
    breed: "Labrador",
    isVaccinated: true
};

Section 7 β€” Type Aliases

type creates a name for any type expression:

// Simple alias
type UserId = string;
type Timestamp = number;
type Callback = () => void;

// Object type (similar to interface)
type User = {
    id: UserId;
    name: string;
    createdAt: Timestamp;
};

// Union
type Status = "active" | "inactive" | "pending";

// Intersection β€” combine multiple types
type AdminUser = User & { role: "admin"; permissions: string[] };

// Function type
type TransformFn = (input: string) => string;
const uppercase: TransformFn = (s) => s.toUpperCase();

// Conditional type (advanced β€” covered in Guide 09)
type IsString<T> = T extends string ? true : false;

Section 8 β€” type vs interface

This is a common question. Both describe object shapes, but have key differences:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              type  vs  interface  comparison                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  type                          β”‚  interface                     β”‚
β”‚  ────────────────              β”‚  ────────────────              β”‚
β”‚  βœ“ Objects AND primitives      β”‚  βœ“ Objects only               β”‚
β”‚  βœ“ Union types                 β”‚  βœ— Can't create unions         β”‚
β”‚  βœ“ Intersection (&)            β”‚  βœ“ Extension (extends)        β”‚
β”‚  βœ— Cannot be reopened          β”‚  βœ“ Declaration merging         β”‚
β”‚  βœ“ Computed properties         β”‚  βœ— Computed properties        β”‚
β”‚  βœ“ More flexible               β”‚  βœ“ More "class-like"           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recommendation: 
  - Use interface for objects/classes you'll extend or implement
  - Use type for unions, intersections, primitives, function types
  - In practice: either works for simple object shapes β€” be consistent
// interface CAN be reopened (declaration merging):
interface Window {
    myCustomThing: string;   // adds to existing Window interface
}

// type CANNOT be reopened:
type Foo = { a: string };
type Foo = { b: string };   // Error! Duplicate identifier 'Foo'

Section 9 β€” Enums

Enums create named sets of constants:

// Numeric enum (default β€” values are 0, 1, 2...)
enum Direction {
    North,    // 0
    South,    // 1
    East,     // 2
    West      // 3
}

let dir: Direction = Direction.North;
Direction[0]           // "North" (reverse mapping)

// String enum (preferred β€” more readable in logs/JSON)
enum Status {
    Pending   = "PENDING",
    Active    = "ACTIVE",
    Completed = "COMPLETED",
    Cancelled = "CANCELLED"
}

let status: Status = Status.Active;
console.log(status);   // "ACTIVE" (not 1 like numeric enum)

// Const enum (compile-time optimization β€” values are inlined)
const enum Region {
    NA = "us-east-1",
    EU = "eu-west-1",
    FE = "us-west-2"
}

// Usage:
function getRegionName(region: Region): string {
    switch (region) {
        case Region.NA: return "North America";
        case Region.EU: return "Europe";
        case Region.FE: return "Far East";
    }
}

Section 10 β€” Type Assertions

Tell TypeScript β€œtrust me, I know the type”:

// as keyword
const inputElement = document.getElementById("email") as HTMLInputElement;
inputElement.value;   // OK β€” TypeScript knows it's HTMLInputElement, not just HTMLElement

// Alternative syntax (avoid in .tsx files β€” conflicts with JSX)
const input2 = <HTMLInputElement>document.getElementById("email");

// When to use: when you know more than TypeScript about the type
const rawData: unknown = JSON.parse(userInput);
const user = rawData as User;   // careful! no runtime check

// Non-null assertion ! (use sparingly)
const element = document.getElementById("main")!;
// The ! says "I guarantee this won't be null"
// TypeScript won't warn anymore, but you'll crash if it IS null

Section 11 β€” Practical TypeScript in a Node.js API

Here’s what TypeScript looks like in a real backend file:

// types.ts β€” define your data shapes
export interface Fleet {
    fleetId: string;
    serviceId: string;
    eventId: string;
    region: string;
    peakTpm: number;
    isActive: boolean;
    createdAt: string;
}

export interface CreateFleetInput {
    fleetId: string;
    serviceId: string;
    region: string;
    peakTpm?: number;    // optional
}

export type FleetStatus = "active" | "inactive" | "scaling";

// fleetService.ts β€” business logic with types
import { Fleet, CreateFleetInput, FleetStatus } from "./types";

export class FleetService {

    async getFleet(fleetId: string, eventId: string): Promise<Fleet | null> {
        const fleet = await db.get({ fleetId, eventId });
        return fleet ?? null;
    }

    async createFleet(input: CreateFleetInput): Promise<Fleet> {
        if (!input.fleetId || !input.serviceId) {
            throw new Error("fleetId and serviceId are required");
        }

        const fleet: Fleet = {
            fleetId:   input.fleetId,
            serviceId: input.serviceId,
            eventId:   "BAU",
            region:    input.region,
            peakTpm:   input.peakTpm ?? 0,
            isActive:  true,
            createdAt: new Date().toISOString()
        };

        await db.put(fleet);
        return fleet;
    }

    async updateStatus(fleetId: string, status: FleetStatus): Promise<void> {
        await db.update(fleetId, { isActive: status === "active" });
    }
}

Section 12 β€” Quick Reference

Type Syntax Use for
String string Text
Number number Integers and decimals
Boolean boolean true/false
Array string[] or Array<string> Lists
Tuple [string, number] Fixed-length mixed array
Object { name: string } Inline object shape
Any any Escape hatch (avoid)
Unknown unknown Safe unknown β€” must narrow
Never never Never occurs / exhaustive
Void void Function returns nothing
Union string | number One of multiple types
Intersection A & B Has all properties of A and B
Literal "GET" | "POST" Specific allowed values
Optional age?: number May be undefined
Readonly readonly id: number Can’t be reassigned
Interface interface User { } Object shape (extendable)
Type alias type Status = "ok" | "err" Any type with a name
Enum enum Dir { North, South } Named constants