Skip to content
Commitpedia
Log in Join & edit

TypeScript basics: types, interfaces, unions and narrowing

TypeScript adds type annotations to JavaScript and checks them before your code runs. These are the core ideas you need to read and write everyday TypeScript.

Not checked by a trusted editor yet Last edited Outdated?

Edit history of TypeScript basics: types, interfaces, unions and narrowing: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
3 min

TypeScript is JavaScript with types. You describe what shape your data has, and the compiler (tsc) or your editor warns you when code does not match, such as calling a method that might not exist. The types are removed before the code runs, so they cost nothing at runtime and also do not check anything at runtime.

Annotations and inference

You annotate function parameters and public APIs. Inside functions, TypeScript usually works out the types on its own:

function greet(name: string, times: number = 1): string {
  return `Hello, ${name}! `.repeat(times);
}

const count = 3;          // inferred as the literal type 3
let title = "Draft";      // inferred as string
const tags: string[] = []; // annotate empty arrays

Object types with interface and type

interface User {
  id: string | number;
  name: string;
  email?: string;            // optional: string | undefined
  readonly createdAt: Date;  // cannot be reassigned
}

type Point = { x: number; y: number };

interface and type overlap a lot for object shapes. A common convention: use interface for object shapes that others may extend, and type for unions, tuples and computed types.

Union types

A union says a value can be one of several types: string | number, or "light" | "dark" for a fixed set of strings. You can only use what all members have in common until you narrow it.

Narrowing

Narrowing is how you go from a broad type to a specific one with ordinary JavaScript checks. TypeScript follows your if statements:

function formatId(id: string | number): string {
  if (typeof id === "number") {
    return id.toString().padStart(6, "0"); // id is number here
  }
  return id.toUpperCase();                 // id is string here
}

Checks that narrow include typeof, instanceof, "key" in obj, equality checks, truthiness checks and Array.isArray.

Discriminated unions

Give each member of a union a shared literal field, and a switch on that field narrows perfectly. Assigning to never in the default branch makes the compiler complain if you add a new shape and forget to handle it:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; width: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rect":
      return shape.width * shape.height;
    default: {
      const unreachable: never = shape;
      throw new Error(`Unknown shape: ${JSON.stringify(unreachable)}`);
    }
  }
}

unknown, any and type guards

Data from JSON.parse, fetch or user input has no guaranteed shape. Type it as unknown, which forces you to check before use, rather than any, which turns checking off. A function returning value is User is a type guard that narrows wherever it returns true:

function isUser(value: unknown): value is User {
  return typeof value === "object" && value !== null && "id" in value && "name" in value;
}

const data: unknown = JSON.parse(text);
if (isUser(data)) console.log(data.name);

For larger payloads, a validation library such as Zod or Valibot checks the data at runtime and produces the TypeScript type for you.

Generics and utility types

Generics let one function work with many types while keeping them connected: function first<T>(items: readonly T[]): T | undefined returns a number for a number array and a User for a user array. Built-in helpers transform types: Partial<User>, Pick<User, "id" | "name">, Omit, Record<string, number> and ReturnType.

Getting started

  • Turn on "strict": true in tsconfig.json from day one; adding it later is much harder.
  • Run npx tsc --noEmit to type-check without producing files.
  • Recent Node versions can run .ts files directly by stripping the types (node app.ts). That skips type checking, so keep tsc in your editor and CI.

Repos in the wild

  • TypeScript microsoft/TypeScript Microsoft's TypeScript language and compiler: typed JavaScript that compiles to plain JavaScript, plus the language service that powers editor features. GitHub

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Commitpedia +3,999 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?