TypeScript Essentials

Basic Types

Type annotations
// Primitives
let name: string = "Evan";
let port: number = 8080;
let active: boolean = true;

// Arrays
let ports: number[] = [22, 80, 443];
let names: Array<string> = ["sw01", "sw02"];

// Tuple — fixed-length, typed positions
let entry: [string, number] = ["sw01", 22];

// Union types — either/or
let id: string | number = "abc-123";
id = 42;  // also valid

Interfaces & Type Aliases

Define object shapes
// Interface — extendable, declaration merging
interface Host {
    name: string;
    ip: string;
    port: number;
    active?: boolean;  // optional
}

// Type alias — more flexible
type HostOrIP = Host | string;
type PortRange = [number, number];

// Extending interfaces
interface NetworkDevice extends Host {
    vlan: number;
    model: string;
}

// Record utility type
type PortMap = Record<string, number>;
const ports: PortMap = { ssh: 22, http: 80 };

Use interface for object shapes that might be extended. Use type for unions, intersections, and mapped types.

Functions

Typed parameters and return values
function add(a: number, b: number): number {
    return a + b;
}

// Optional and default parameters
function connect(host: string, port: number = 443, timeout?: number): void {
    console.log(`${host}:${port}`);
}

// Function type
type Predicate = (item: Host) => boolean;
const isActive: Predicate = (h) => h.active === true;

Generics

Reusable typed components
function first<T>(arr: T[]): T | undefined {
    return arr[0];
}

const n = first([1, 2, 3]);       // type: number | undefined
const s = first(["a", "b"]);      // type: string | undefined

// Generic constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const host: Host = { name: "sw01", ip: "10.50.1.10", port: 22 };
const name = getProperty(host, "name");  // type: string

Utility Types

Built-in type transformations
// Partial — all properties optional
function updateHost(host: Host, updates: Partial<Host>) {
    return { ...host, ...updates };
}

// Required — all properties required
type StrictHost = Required<Host>;

// Pick — subset of properties
type HostSummary = Pick<Host, "name" | "ip">;

// Omit — exclude properties
type PublicHost = Omit<Host, "port">;

// Readonly — immutable
const config: Readonly<Host> = { name: "sw01", ip: "10.50.1.10", port: 22 };
// config.port = 443;  // Error: readonly

// NonNullable — remove null and undefined
type Defined = NonNullable<string | null | undefined>;  // string

Narrowing

Type guards refine types at runtime
function process(input: string | number) {
    if (typeof input === "string") {
        console.log(input.toUpperCase());  // TS knows it's string here
    } else {
        console.log(input.toFixed(2));     // TS knows it's number here
    }
}

// Discriminated unions
type Result =
    | { status: "ok"; data: Host }
    | { status: "error"; message: string };

function handle(result: Result) {
    switch (result.status) {
        case "ok":
            console.log(result.data.name);    // TS narrows to ok branch
            break;
        case "error":
            console.log(result.message);      // TS narrows to error branch
            break;
    }
}

Discriminated unions with a shared literal property (status) are the TypeScript equivalent of Rust enums with data.

tsconfig.json Essentials

Common configuration
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "outDir": "dist",
    "declaration": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

"strict": true enables all strict checks. Start with it on — turning it off later is easier than adding it to a large codebase.