Skip to content

TypeScript Reference: Types, Generics, Narrowing, tsconfig & Advanced Patterns

TypeScript is JavaScript with static types. It compiles to plain JavaScript and runs anywhere JS runs. The key skills: understanding the type system (interfaces vs types, generics, utility types, narrowing) and configuring tsconfig.json correctly for your environment.

1. Types, Interfaces & Union Types

Core type system — primitives, objects, unions, intersections
// Primitives:
let name: string = "Alice";
let age: number = 30;
let active: boolean = true;
let data: unknown = fetchData();     // unknown forces type checking before use
let value: any = "anything";         // any = escape hatch (avoid in new code)
let nothing: void = undefined;       // function return type for no value
let impossible: never;               // function that never returns (throws/infinite loop)

// Arrays and tuples:
let names: string[] = ["Alice", "Bob"];
let pair: [string, number] = ["Alice", 30];   // tuple: fixed-length, typed positions

// Object types (interface):
interface User {
  id: number;
  name: string;
  email?: string;                    // optional property
  readonly createdAt: Date;          // can't be reassigned after creation
}

// Object types (type alias — more flexible than interface for complex types):
type Point = { x: number; y: number };
type ID = string | number;           // union type

// Union and intersection:
type Status = "pending" | "active" | "inactive";  // string literal union
type AdminUser = User & { role: "admin"; permissions: string[] };  // intersection

// Discriminated union (exhaustive pattern matching):
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "rectangle": return shape.width * shape.height;
    // TypeScript warns if you miss a case (exhaustiveness check)
  }
}

2. Generics

Type-safe reusable functions, classes, and utility types
// Generic function:
function first(arr: T[]): T | undefined {
  return arr[0];
}
const n = first([1, 2, 3]);   // TypeScript infers T = number

// Generic with constraint (only objects with a 'name' field):
function greet(obj: T): string {
  return `Hello, ${obj.name}`;
}

// Generic interface:
interface ApiResponse {
  data: T;
  status: number;
  message: string;
}
type UserResponse = ApiResponse;
type ListResponse = ApiResponse;

// Built-in utility types (use these — don't reinvent them):
type PartialUser = Partial;           // all fields optional
type RequiredUser = Required;         // all fields required
type ReadonlyUser = Readonly;         // all fields readonly
type UserKeys = keyof User;                 // "id" | "name" | "email" | "createdAt"
type PickedUser = Pick;       // subset of fields
type OmittedUser = Omit;         // remove fields
type RecordType = Record;    // { [key: string]: number }
type NonNullable = T extends null | undefined ? never : T;
type ReturnType;       // extract return type of a function
type Parameters;       // extract parameter types tuple

3. Type Narrowing & Guards

typeof, instanceof, in, custom type guards, and assertion functions
// TypeScript narrows types automatically in branches:
function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();   // TypeScript knows it's string here
  }
  return value * 2;               // TypeScript knows it's number here
}

// instanceof narrowing:
function formatError(error: unknown) {
  if (error instanceof Error) {
    return error.message;         // TypeScript knows it's Error here
  }
  return String(error);
}

// 'in' narrowing (check if property exists):
type Cat = { meow(): void };
type Dog = { bark(): void };
function makeSound(animal: Cat | Dog) {
  if ("meow" in animal) {
    animal.meow();                // Cat
  } else {
    animal.bark();                // Dog
  }
}

// Custom type guard (predicate function):
function isUser(obj: unknown): obj is User {
  return typeof obj === "object" && obj !== null &&
         "id" in obj && "name" in obj;
}
// Usage:
const data: unknown = JSON.parse(response);
if (isUser(data)) {
  console.log(data.name);        // TypeScript knows it's User here
}

// Non-null assertion (use sparingly — bypasses null check):
const el = document.getElementById("app")!;   // ! = "I know this is not null"

4. tsconfig.json — Key Options

Strict mode, module resolution, paths, and environment targets
// tsconfig.json — recommended base:
{
  "compilerOptions": {
    "target": "ES2022",              // output JS version (ES2022 for Node.js 18+)
    "module": "NodeNext",            // module system: NodeNext (Node 18+), ESNext, CommonJS
    "moduleResolution": "NodeNext",  // MUST match 'module' for Node.js ESM
    "lib": ["ES2022"],               // type definitions to include
    "outDir": "./dist",              // where compiled JS goes
    "rootDir": "./src",              // source directory
    "strict": true,                  // ALWAYS enable (enables 8 checks at once)
    "noUncheckedIndexedAccess": true, // arr[0] is T | undefined, not T (catches OOB)
    "exactOptionalPropertyTypes": true, // {a?: string} means string, not string|undefined
    "noImplicitReturns": true,       // function must return in all code paths
    "noFallthroughCasesInSwitch": true, // switch case fallthrough is an error

    // Path aliases (avoid ../../.. imports):
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],              // import '@/utils/db' → src/utils/db
      "@shared/*": ["src/shared/*"]
    },

    // Declaration files (for publishing a library):
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

// For React projects (Vite):
// "jsx": "react-jsx"   (not "react" — avoids importing React in every file)
// "lib": ["ES2022", "DOM", "DOM.Iterable"]
// "module": "ESNext", "moduleResolution": "Bundler"

// Check types without emitting:
// tsc --noEmit   (use in CI to type-check without building)

5. Advanced Patterns

Mapped types, template literals, conditional types, and declaration merging
// Mapped types (transform every property):
type Optional = { [K in keyof T]?: T[K] };       // same as Partial
type Nullable = { [K in keyof T]: T[K] | null };
type Getters = { [K in keyof T as `get${Capitalize}`]: () => T[K] };
// Getters<{name: string}> → {getName: () => string}

// Template literal types (string manipulation at type level):
type EventName = "click" | "focus" | "blur";
type Handler = `on${Capitalize}`;  // "onClick" | "onFocus" | "onBlur"

// Conditional types:
type IsArray = T extends any[] ? true : false;
type Flatten = T extends Array ? U : T;
// Flatten = string; Flatten = string

// Infer keyword (extract types from complex shapes):
type UnpackPromise = T extends Promise ? U : T;
// UnpackPromise> = User

// Declaration merging (augmenting interfaces across files):
// Useful for extending global types (e.g. Express Request):
declare namespace Express {
  interface Request {
    user?: User;                    // add user property to all Express requests
  }
}

// Const assertions (literal type preservation):
const config = {
  endpoint: "https://api.example.com",
  timeout: 5000
} as const;
// config.endpoint is "https://api.example.com" (not string)
// config is Readonly — no reassignment allowed

// Satisfies operator (TS 4.9+): validate type without widening:
const palette = {
  red: [255, 0, 0],
  green: "#00ff00"
} satisfies Record;
// palette.red is number[], not string | number[] (keeps specifics)

Track TypeScript and Node.js releases.
ReleaseRun monitors Node.js, TypeScript, React, and 13+ technologies.

Related: JavaScript ES6+ Reference | Node.js Reference | React Reference | TypeScript EOL Tracker

🔍 Free tool: npm Package Health Checker — check any npm package for outdated TypeScript types, EOL status, and known vulnerabilities instantly.

📦 Free tool: package.json Health Checker — paste your entire package.json and get A–F health grades for all dependencies at once, including TypeScript, React, and build tools.

📦 Free tool: package.json Health Checker — paste your entire package.json and get A–F grades for all dependencies at once — TypeScript, ts-node, types packages, build tools.

📦 Free tool: package.json Health Checker — paste your entire package.json and get A–F health grades for every dependency at once, including TypeScript, ts-node, and @types/* packages.

Founded

2023 in London, UK

Contact

hello@releaserun.com