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
readonlyto 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: numbermeans 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",
};
companyis a required property.- Any other string key is allowed, but must have a
stringvalue.
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:
- Other interfaces (most common case).
- Object types (via
typealiases).
interface Person {
name: string;
}
interface Employee extends Person {
id: number;
department: string;
}
let emp: Employee = {
id: 1,
name: "Alice",
department: "HR",
};
Employeemust have everything fromPersonplus 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
extendsInterface / 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.