Skip to main content

Type Basics

What are Types?

Types describe the shape of data in your program. TypeScript adds a type system on top of JavaScript that catches bugs at compile time instead of at runtime.

// JavaScript: this bug only shows up at runtime
function double(x) {
  return x * 2;
}
double("hello"); // NaN — no error until you run it

// TypeScript: caught immediately
function double(x: number): number {
  return x * 2;
}
double("hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

Primitive Types

TypeScript has the same primitive types as JavaScript, plus a few extras:

// String
let name: string = "Sabaoon";

// Number (integers and floats are both 'number')
let age: number = 25;
let price: number = 9.99;

// Boolean
let isActive: boolean = true;

// Null and Undefined
let nothing: null = null;
let notDefined: undefined = undefined;

// BigInt
let huge: bigint = 100n;

// Symbol
let id: symbol = Symbol("id");

Type Annotations vs. Type Inference

You don't always need to write types explicitly. TypeScript can infer them from the value you assign:

// Explicit annotation — you write the type
let city: string = "Peshawar";

// Type inference — TypeScript figures it out
let country = "Pakistan"; // inferred as string
let count = 42;           // inferred as number
let done = false;         // inferred as boolean

Rule of thumb: let TypeScript infer when the type is obvious from the value. Add annotations when the type isn't clear, or when you want to be explicit about a function's contract.

Arrays

There are two syntaxes for typed arrays:

// Bracket syntax (preferred)
let scores: number[] = [95, 87, 92];
let names: string[] = ["Alice", "Bob"];

// Generic syntax
let ids: Array<number> = [1, 2, 3];

// TypeScript infers element types from the values
let colors = ["red", "green", "blue"]; // string[]

// Mixed arrays use union types
let mixed: (string | number)[] = ["hello", 42, "world"];

Objects

You can type objects inline or with named types:

// Inline object type
let user: { name: string; age: number; email: string } = {
  name: "Sabaoon",
  age: 25,
  email: "sabaoon@example.com",
};

// Optional properties use ?
let config: { debug?: boolean; verbose?: boolean } = {};
config.debug = true; // fine
config.verbose = undefined; // also fine

// Readonly properties
let point: { readonly x: number; readonly y: number } = { x: 10, y: 20 };
// point.x = 5; // Error: Cannot assign to 'x' because it is a read-only property

Tuples

Tuples are fixed-length arrays where each position has a specific type:

// A tuple of [string, number]
let pair: [string, number] = ["age", 25];

// Destructuring works as expected
let [label, value] = pair; // label: string, value: number

// Named tuples (TypeScript 4.0+) — labels are for documentation only
type Coordinate = [x: number, y: number];
let point: Coordinate = [10, 20];

Enums

Enums define a set of named constants:

// Numeric enum (values auto-increment from 0)
enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

let move: Direction = Direction.Up;

// String enum (each value must be explicitly set)
enum Status {
  Active = "ACTIVE",
  Inactive = "INACTIVE",
  Pending = "PENDING",
}

let userStatus: Status = Status.Active;

Tip: Many TypeScript developers prefer union types (type Direction = "up" | "down" | "left" | "right") over enums because they produce less JavaScript output and are simpler to use.

The any, unknown, and never Types

// any — disables type checking (avoid when possible)
let anything: any = "hello";
anything = 42;
anything.foo.bar; // no error, but could crash at runtime

// unknown — safe alternative to any (must narrow before use)
let input: unknown = "hello";
// input.toUpperCase(); // Error: Object is of type 'unknown'
if (typeof input === "string") {
  input.toUpperCase(); // OK after narrowing
}

// never — represents values that never occur
function throwError(message: string): never {
  throw new Error(message);
}

Type Assertions

Sometimes you know more about a type than TypeScript does:

// The 'as' syntax (preferred)
let input = document.getElementById("name") as HTMLInputElement;
input.value = "hello";

// Angle bracket syntax (not usable in TSX files)
let input2 = <HTMLInputElement>document.getElementById("name");

Use assertions sparingly — they bypass the type checker, so an incorrect assertion can cause runtime bugs.