TypeScript Index Signatures
Define types for dynamic property names
📇 What are Index Signatures?
Index signatures allow you to define types for objects with dynamic property names. They're perfect when you don't know property names in advance but know their types.
type StringMap = { [key: string]: string };
const colors: StringMap = { red: "#FF0000", blue: "#0000FF" };
Key Concepts
String Keys
Most common index signature type
type Dict = { [key: string]: number };
const scores: Dict = { alice: 95 };
Number Keys
For array-like objects
type NumDict = { [index: number]: string };
const items: NumDict = { 0: "first" };
Mixed Properties
Combine with known properties
type User = {
name: string;
[key: string]: any;
};
Flexible Objects
Handle unknown properties
const config: { [k: string]: boolean } = {
debug: true, verbose: false
};
🔹 Basic String Index Signature
Create objects with any string keys:
type PhoneBook = {
[name: string]: string;
};
const contacts: PhoneBook = {
"Alice": "555-1234",
"Bob": "555-5678",
"Charlie": "555-9012"
};
console.log(contacts["Alice"]); // "555-1234"
console.log(contacts["Bob"]); // "555-5678"
contacts["Diana"] = "555-3456"; // ✓ Valid
🔹 Number Index Signature
Use numeric keys for array-like structures:
type NumberArray = {
[index: number]: string;
};
const weekdays: NumberArray = {
0: "Monday",
1: "Tuesday",
2: "Wednesday",
3: "Thursday",
4: "Friday"
};
console.log(weekdays[0]); // "Monday"
console.log(weekdays[4]); // "Friday"
🔹 Combining with Known Properties
Mix index signatures with specific properties:
type Config = {
version: string; // Required property
[key: string]: string; // Any other string properties
};
const appConfig: Config = {
version: "1.0.0",
theme: "dark",
language: "en",
timezone: "UTC"
};
console.log(appConfig.version); // "1.0.0"
console.log(appConfig.theme); // "dark"
🔹 Index Signature with Union Types
Allow multiple value types:
type MixedData = {
[key: string]: string | number | boolean;
};
const userData: MixedData = {
name: "Alice",
age: 30,
isActive: true,
email: "[email protected]",
score: 95.5
};
console.log(userData.name); // "Alice"
console.log(userData.age); // 30
console.log(userData.isActive); // true
🔹 Readonly Index Signatures
Prevent modifications to properties:
type ReadonlyDict = {
readonly [key: string]: number;
};
const constants: ReadonlyDict = {
PI: 3.14159,
E: 2.71828
};
console.log(constants.PI); // 3.14159
// constants.PI = 3.14; // ✗ Error: Cannot assign to readonly
🔹 Practical Example: Cache System
Build a type-safe cache:
type Cache<T> = {
[key: string]: T;
};
class DataCache<T> {
private cache: Cache<T> = {};
set(key: string, value: T): void {
this.cache[key] = value;
}
get(key: string): T | undefined {
return this.cache[key];
}
has(key: string): boolean {
return key in this.cache;
}
}
const userCache = new DataCache<{ name: string; age: number }>();
userCache.set("user1", { name: "Alice", age: 30 });
console.log(userCache.get("user1")); // { name: "Alice", age: 30 }
🔹 Important Constraints
Be aware of index signature limitations:
// ✗ Error: Property type must match index signature
type Invalid = {
[key: string]: number;
name: string; // Error! string is not assignable to number
};
// ✓ Valid: Property type matches or is subset
type Valid = {
[key: string]: string | number;
name: string; // ✓ OK: string is part of union
age: number; // ✓ OK: number is part of union
};
💡 Key Takeaways
- Index signatures define types for dynamic property names
-
Use
[key: string]for string keys or[index: number]for numeric keys - Can combine with known properties for flexible types
- All known properties must match the index signature type