Skip to main content

TypeScript Index Signatures

An index signature lets you describe an object whose property names you do not know in advance, while still saying what type every value must have.


Understanding Index Signatures in TypeScript

Index signatures give you a way to type objects with dynamic property names without losing type safety.

They tell TypeScript what type of value comes back when you read a property with bracket notation (obj[key]), even when the exact property names are not known ahead of time.

Key ideas:

  • Dynamic property access: handle objects with any property names.
  • Type safety: every dynamic property must hold the same kind of value.
  • Flexible data structures: model dictionaries, maps and other lookup tables.
  • Early errors: mistakes with value types are caught at compile time.

Basic Index Signatures

String index signatures. Use an index signature when you do not know the property names in advance, but you do know the shape of the values.

The syntax uses brackets: [key: type] describes the type of the property names (the keys), and after the colon comes the type of the values those properties can hold.

Number index signatures. TypeScript supports number keys as well as string keys.

Note: In JavaScript, all object keys are stored as strings, even numeric ones. TypeScript still tells the two apart to help catch logical errors when you mix up arrays and objects.

String index signature

StringDictionary accepts any string key, but every value must be a string. Reading, adding and updating keys all work, while assigning a number is an error.

Prompt
Show a string index signature with an interface StringDictionary whose keys are strings and whose values are strings. Create a names object of that type with firstName: "Alice", lastName: "Smith" and the key "100" mapped to "One Hundred", log the three values with bracket access, then add an age key with the string "30". Leave a commented-out line showing that assigning the number 30 would be a type error.
Output: TypeScript file and captured output

Number index signature

NumberDictionary uses number keys. Because the value type is any, each entry can hold a different kind of value.

Prompt
Show a number index signature with an interface NumberDictionary whose keys are numbers and whose values are any. Create a scores object with 0: "Zero", 1: 100 and 2: true, log the three entries by numeric index, then add index 3 holding the object { passed: true }.
Output: TypeScript file and captured output

Advanced Index Signature Patterns

Mixed property types. You can combine an index signature with explicitly named properties. The named properties are required; everything else must match the index signature.

Important: When you mix explicit properties with an index signature, the type of every explicit property must be assignable to the index signature's value type.

Readonly index signatures. Add readonly to an index signature to stop values from being changed after the object is created.

For constraining key sets and transforming shapes, see the Mapped Types lesson.

Mixed property types

UserInfo requires name and age, and allows any other property as long as its value is a string or a number.

Prompt
Combine explicit properties with an index signature in a UserInfo interface: required name: string and age: number, plus a [key: string]: string | number signature for any other property. Create a valid user with name: "Alice", age: 30, address: "123 Main St" and zipCode: 12345. Add a commented-out invalidUser showing that age: "thirty" and isAdmin: true would each be type errors. The file prints nothing.
Output: TypeScript file and captured output

Readonly index signature

Reading names[0] is fine, but writing to any index of a ReadOnlyStringArray is a compile error.

Prompt
Declare a ReadOnlyStringArray interface with a readonly [index: number]: string index signature. Assign the array ["Alice", "Bob", "Charlie"] to a names variable of that type, log names[0], and leave a commented-out assignment to names[0] explaining that writing through a readonly index signature is a type error.
Output: TypeScript file and captured output

Real-World Examples

API response handling. Index signatures are handy for API responses where the resource names and extra metadata fields are not fixed.

API response handling

ApiResponse<T> lets data hold any resource name mapped to an array of T, and lets meta carry extra fields beyond page and total.

Prompt
Model a generic API response with index signatures: an ApiResponse<T> interface whose data maps any resource name ([resourceType: string]) to T[], and whose meta has page: number, total: number plus a [key: string]: any signature for extra metadata. Declare a User interface (id, name, email), build an apiResponse: ApiResponse<User> with two users (Alice and Bob) and meta page: 1, total: 2, timestamp: "2023-01-01T00:00:00Z", then log the first user's name.
Output: TypeScript file and captured output

Best Practices

Do:

  • Use index signatures for collections with dynamic keys.
  • Combine them with explicit properties for the fields you do know.
  • Keep value types specific — avoid any.
  • Use readonly when the values should not change.

Don't:

  • Reach for an index signature when the keys are known — prefer a fixed interface.
  • Forget that every property, including explicit ones, must fit the index signature's value type.
  • Reinvent mapped types — use them for shape transformations.

Common Pitfalls

A common mistake is declaring an explicit property whose type does not fit the index signature. Widen the index signature's value type to fix it.

Conflicting property and index types

ConflictingTypes fails because name: string does not fit the number index signature; FixedTypes widens the value type to number | string so both properties are allowed.

script.ts
  interface ConflictingTypes {
    [key: string]: number;
    name: string; // Error: not assignable to string index type 'number'
  }

  interface FixedTypes {
    [key: string]: number | string;
    name: string;  // OK
    age: number;   // OK
  }

Index Signatures vs Record

Use an index signature when the keys are flexible or dynamic, and when you also need to mix in other properties.

Use Record<K, T> for concise, simple mappings from one key type to one value type.

Index signature and Record side by side

StringMap and StringRecord describe the same shape — any string key mapped to a string value — written two different ways.

script.ts
// Index signature
interface StringMap {
  [key: string]: string;
}

// Record
type StringRecord = Record<string, string>;

Conclusion

Use index signatures when keys are dynamic but the value shape is consistent.

Combine them with explicit properties where possible, and see the Mapped Types and Utility Types lessons for more advanced transformations.