Skip to main content

Objects

Object Type Annotations

In TypeScript, Object Type Annotations let you explicitly describe the shape of an object — i.e., what properties it has and what types those properties are.

This helps TypeScript check correctness at compile time and prevents mistakes like missing or wrongly typed properties.

let obj: {
key1: Type1;
key2: Type2;
};
  • Keys represent property names.
  • Values represent property types.

Readonly Properties

  • Use readonly to prevent modification after initialization.
  • It enforces immutability at the type level (compile-time safety).
let config: {
readonly apiKey: string;
mode: "dev" | "prod";
};

config = { apiKey: "12345", mode: "dev" };

// config.apiKey = "67890"; ❌ Error (readonly)
config.mode = "prod"; // ✅ Allowed

apiKey cannot be reassigned after initialization.

Index Signatures

Use index signatures when you don’t know all property names ahead of time.

let salaries: {
[employeeName: string]: number;
};

salaries = {
Alice: 50000,
Bob: 60000,
Charlie: 55000,
};

Any string key is allowed, and its value must be a number.

Number Index Signature

You can use numbers as keys.

interface NumberArray {
[index: number]: string;
}

const myArray: NumberArray = ["apple", "banana", "cherry"];

console.log(myArray[0]); // apple
  • Here, index: number means array-like indexing.
  • All elements must be strings.

Note: In JavaScript, object keys are always strings under the hood, so number index signatures are converted to strings internally.

Mixed Keys (String + Number)

If you mix string and number index signatures, the number index must be a subtype of the string index (since JavaScript converts numbers → strings).

interface MixedDictionary {
[key: string]: string; // string keys allowed
[index: number]: string; // number keys allowed
}

const data: MixedDictionary = {
a: "alpha",
1: "one",
};
  • Both string and number keys are allowed.
  • Values must be string.

Restricting Index Signatures

You can still add specific properties alongside index signatures.

interface EmployeeDirectory {
[id: string]: string; // index signature
company: string; // fixed property
}

const employees: EmployeeDirectory = {
company: "TechCorp",
e101: "Alice",
e102: "Bob",
};
  • company is a required property.
  • Any other string key is allowed, but must have a string value.

API Response Map

Index signatures are common when dealing with dynamic data structures like dictionaries, configuration maps, or JSON responses.

interface ApiResponse {
[field: string]: string | number | boolean;
}

const response: ApiResponse = {
status: "success",
code: 200,
isLoggedIn: true,
};

Function as a Property

You can annotate functions inside objects.

let mathUtils: {
add: (a: number, b: number) => number;
square: (x: number) => number;
};

mathUtils = {
add: (a, b) => a + b,
square: (x) => x * x,
};

Ensures methods inside objects follow specific signatures.

Interfaces

An interface in TypeScript is a way to define the shape of an object.

It tells TypeScript:

  • What properties an object must have.
  • What types those properties should be.
  • Optionally, what methods the object should include.

Think of it as a contract: if a variable claims to follow an interface, it must satisfy that contract.

interface InterfaceName {
propertyName: Type;
anotherProperty: Type;
}

Extending Interfaces

TypeScript allows interfaces to extend:

  1. Other interfaces (most common case).
  2. Object types (via type aliases).
interface Person {
name: string;
}

interface Employee extends Person {
id: number;
department: string;
}

let emp: Employee = {
id: 1,
name: "Alice",
department: "HR",
};
  • Employee must have everything from Person plus its own properties.
  • You can extend multiple interfaces (similar to multiple inheritance).
type Person = {
name: string;
age: number;
};

interface Employee extends Person {
employeeId: number;
}

Interface vs. Type Extension

  • Interface extends Interface / Type → only works with object-like shapes.
  • Type alias & (intersection) → combines multiple types.
type Person = {
name: string;
age: number;
};

type Address = {
city: string;
country: string;
};

// Intersection
type Employee = Person &
Address & {
employeeId: number;
};

Declaration Merging (Special Feature of Interfaces)

If you declare the same interface name twice, TypeScript merges them.

interface Car {
brand: string;
}

interface Car {
model: string;
}

let myCar: Car = {
brand: "Toyota",
model: "Corolla",
};

Useful when extending library types without modifying them directly.