Type System

TypeScript’s type system catches errors at compile time without changing how your code runs.

Type Inference

TypeScript infers types from values. You don’t need to annotate everything.

let name = "Alice";      // inferred as string
let count = 42;           // inferred as number
let items = [1, 2, 3];   // inferred as number[]

Tip: Let TypeScript infer when it can. Only add annotations when the compiler can’t figure it out or when you want to be explicit about a public API.

Type Annotations

Explicitly declare types when inference isn’t enough:

function greet(name: string): string {
  return "Hello, " + name;
}

greet("Alice"); // OK
greet(42);      // Error: Argument of type 'number' is not assignable to parameter of type 'string'

Gotcha: Over-annotating makes code verbose. If a variable is initialized on the same line, the annotation is usually redundant: let x: number = 5 is the same as let x = 5.

Primitive Types

let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let missing: null = null;
let notDefined: undefined = undefined;

Arrays and Tuples

let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10]; // fixed length, fixed types per position

Union Types

A value that can be one of several types:

type ID = string | number;

function printId(id: ID) {
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // TypeScript knows it's a string here
  } else {
    console.log(id);               // TypeScript knows it's a number here
  }
}

Intersection Types

Combine multiple types into one:

type User = { name: string; age: number };
type DBRecord = { id: string };
type UserRecord = User & DBRecord;

// UserRecord has: name, age, and id

Structural Typing

TypeScript checks structure, not names. If it has the right shape, it’s compatible.

interface Duck {
  walk: () => void;
  quack: () => void;
}

let bird = {
  walk: () => console.log("walking"),
  quack: () => console.log("quacking"),
  fly: () => console.log("flying"), // extra property is fine
};

let duck: Duck = bird; // OK - bird has walk and quack

Gotcha: Structural typing means extra properties are allowed when assigning variables. But object literals get stricter checking - TypeScript flags unknown properties on them.