What youβll learn: What TypeScript is and why companies use it. Type annotations, interfaces, enums, and the key difference between
typeandinterface. 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 |