TYPESCRIPT

TypeScript Index Signature: Syntax, Usage, and Examples

An index signature in TypeScript lets you define types for dynamically named object properties. It provides a way to enforce type safety when working with objects where the property names are unknown ahead of time. This is useful when handling key-value structures like dictionaries or maps.

How to Use Index Signatures in TypeScript

The syntax for defining an index signature follows this pattern:

type ObjectType = {
  [key: string]: number;
};

This defines an object type where all property names are strings, and their values must be numbers. You can then use this type to create objects:

const scores: ObjectType = {
  Alice: 85,
  Bob: 92,
  Charlie: 78,
};

console.log(scores["Alice"]); // 85

Defining Index Signatures in Interfaces

You can also use an index signature inside an interface:

interface UserAges {
  [name: string]: number;
}

const userAges: UserAges = {
  Alice: 30,
  Bob: 25,
};

console.log(userAges.Bob); // 25

Using a More Specific Key Type

If you need to restrict the keys to a set of predefined strings, use a union type:

type Statuses = "active" | "inactive" | "pending";

interface StatusMap {
  [key in Statuses]: boolean;
}

const userStatus: StatusMap = {
  active: true,
  inactive: false,
  pending: true,
};

console.log(userStatus.pending); // true

This ensures that only "active", "inactive", or "pending" can be used as keys.

When to Use Index Signatures in TypeScript

Use an index signature when you need a flexible object structure with unknown or dynamic property names. They are particularly useful in cases like:

  • Dynamic key-value storage: When working with API responses, logs, or user-generated data, where property names are not predefined.
  • Dictionaries and lookup tables: If you need to store a set of related values, such as user permissions or configuration settings.
  • Mapping enums or predefined values: If you want to enforce a specific set of keys but allow flexibility in their values.

Index signatures keep TypeScript's type safety while allowing object keys to vary.

Examples of Index Signatures in TypeScript

Defining a Dynamic Object

A common use case for an index signature is when handling an object with arbitrary properties. Here’s an example:

interface Inventory {
  [itemName: string]: number;
}

const storeInventory: Inventory = {
  apples: 50,
  bananas: 30,
  oranges: 20,
};

console.log(storeInventory.bananas); // 30

Using Index Signatures in a Class

You can also use an index signature inside a class to store dynamic properties:

class Settings {
  [key: string]: string;

  constructor() {
    this.theme = "dark";
  }
}

const userSettings = new Settings();
userSettings.language = "English";

console.log(userSettings.language); // "English"

This approach allows settings to be added dynamically while keeping type safety.

Combining Index Signatures with Other Properties

You can mix explicitly defined properties with an index signature:

interface User {
  id: number;
  name: string;
  [extra: string]: string | number;
}

const user: User = {
  id: 1,
  name: "Alice",
  age: 30, // Allowed due to the index signature
  country: "USA",
};

console.log(user.age); // 30

The object still enforces id and name while allowing additional properties of type string | number.

Enforcing a Read-Only Index Signature

If you need an immutable object, use readonly with the index signature:

interface ReadonlyConfig {
  readonly [key: string]: string;
}

const config: ReadonlyConfig = {
  theme: "light",
  language: "English",
};

// config.theme = "dark"; // Error: Cannot assign to 'theme' because it is a read-only property

This ensures the properties cannot be modified after assignment.

Learn More About Index Signatures in TypeScript

Optional Index Signatures

By default, index signatures require every key to have a corresponding value. If you want to allow undefined values, use the optional modifier:

interface OptionalIndex {
  [key: string]: string | undefined;
}

const userProfile: OptionalIndex = {
  username: "johndoe",
  email: undefined, // Allowed
};

Using Record<T, U> Instead of Index Signatures

TypeScript provides a built-in utility type called Record<T, U> that works similarly to an index signature:

type UserRoles = Record<string, string>;

const roles: UserRoles = {
  admin: "Full Access",
  editor: "Edit Access",
};

console.log(roles.admin); // "Full Access"

Record<string, string> is equivalent to { [key: string]: string } but is more concise.

Restricting Key Types in Index Signatures

To prevent invalid keys, use keyof with an index signature:

type RoleKeys = "admin" | "editor" | "viewer";

interface RoleMap {
  [key in RoleKeys]: string;
}

const userRoles: RoleMap = {
  admin: "Manage everything",
  editor: "Edit content",
  viewer: "Read-only",
};

This approach enforces that only "admin", "editor", and "viewer" can be used as keys.

Handling Index Signatures in Interfaces vs. Classes

TypeScript handles index signatures slightly differently in interfaces and classes. When using a class, you may need to define an explicit index signature:

class UserConfig {
  [key: string]: string;

  constructor(public name: string) {
    this.theme = "dark";
  }
}

const settings = new UserConfig("Alice");
settings.language = "English";

console.log(settings.language); // "English"

Avoiding Index Signature Errors

If an object does not define an index signature but is used as one, TypeScript will throw an error:

interface UserInfo {
  name: string;
}

const userInfo: UserInfo = { name: "Bob" };

// console.log(userInfo["age"]); // Error: Property 'age' does not exist on type 'UserInfo'

To fix this, add an explicit index signature or use type assertion:

console.log((userInfo as any)["age"]); // No error, but loses type safety

Index signatures give you the flexibility to define dynamic objects while maintaining TypeScript's type safety. You can use them to create dictionaries, enforce specific key patterns, and build scalable, reusable types.

Learn TypeScript for Free
Start learning now
button icon
To advance beyond this tutorial and learn TypeScript by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH