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

🧠 Test Your Knowledge

What does [key: string]: number mean?