bugl
bugl
HomeLearnPatternsPathsSearchPremium
HomeLearnPatternsPaths
Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript Index Signatures

Understanding Index Signatures in TypeScript

Index signatures in TypeScript provide a powerful way to define types for objects with dynamic property names while maintaining type safety.

They allow you to specify the types of values that can be accessed via bracket notation ( obj[key] ), even when the exact property names aren't known in advance.

Key Concepts

  • Dynamic Property Access : Handle objects with arbitrary property names
  • Type Safety : Ensure consistent value types across dynamic properties
  • Flexible Data Structures : Model dictionaries, maps, and other dynamic data
  • Runtime Safety : Catch type-related errors at compile time

String Index Signatures

Index signatures in TypeScript allow you to define types for objects where you don't know the property names in advance, but you do know the shape of the values.

An index signature defines the types for properties accessed via an index like obj[key] .

Example

// This interface represents an object with string keys and string values interface StringDictionary { [key: string]: string;
}
// Creating a compliant object
const names: StringDictionary = {
  firstName: "Alice", lastName: "Smith", "100": "One Hundred"
};
// Accessing properties console.log(names["firstName"]); // "Alice" console.log(names["lastName"]); // "Smith" console.log(names["100"]); // "One Hundred" // Adding new properties dynamically names["age"] = "30"; // This would cause an error // names["age"] = 30; // Error: Type 'number' is not assignable to type 'string'

The index signature syntax uses brackets [key: type] to describe the types of the property names (or keys) that are allowed, followed by the type of values these properties can have.

Number Index Signatures

TypeScript supports both string and number index signatures:

Example

// Object with number indexes interface NumberDictionary { [index: number]: any;
}
const scores: NumberDictionary = {
  0: "Zero", 1: 100, 2: true
};
console.log(scores[0]); // "Zero"
console.log(scores[1]); // 100
console.log(scores[2]); // true
// Adding a complex object scores[3] = { passed: true };

Note

In JavaScript, all object keys are stored as strings, even numeric ones.

However, TypeScript makes a distinction to help catch logical errors when using arrays vs objects.

Mixed Property Types

You can combine index signatures with explicit property declarations:

Example

interface UserInfo {
  name: string; // Required property with specific name
  age: number;  // Required property with specific name
  [key: string]: string | number; // All other properties must be string or number
}
const user: UserInfo = {
  name: "Alice", // Required
  age: 30,      // Required
  address: "123 Main St", // Optional
  zipCode: 12345 // Optional
};
// This would cause an error // const invalidUser: UserInfo = { //  name: "Bob", //  age: "thirty", // Error: Type 'string' is not assignable to type 'number' //  isAdmin: true  // Error: Type 'boolean' is not assignable to type 'string | number' // };

Important: When combining explicit properties with an index signature, the types of explicit properties must be assignable to the index signature's value type.

ReadOnly Index Signatures

You can make index signatures read-only to prevent modification after creation:

Example

interface ReadOnlyStringArray {
  readonly [index: number]: string;
}
const names: ReadOnlyStringArray = ["Alice", "Bob", "Charlie"];
console.log(names[0]); // "Alice"
// This would cause an error // names[0] = "Andrew"; // Error: Index signature in type 'ReadOnlyStringArray' only permits reading

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

API Response Handling

Example

// Type for API responses with dynamic keys interface ApiResponse<T> { data: { [resourceType: string]: T[];  // e.g., { "users": User[], "posts": Post[] }
};
meta: {
  page: number;
  total: number;
  [key: string]: any;  // Allow additional metadata
};
}
// Example usage with a users API interface User { id: number; name: string; email: string;
}
// Mock API response
const apiResponse: ApiResponse<User> = {
  data: {
    users: [ { id: 1, name: "Alice", email: "alice@example.com" }, { id: 2, name: "Bob", email: "bob@example.com" }
    ]
}, meta: {
page: 1, total: 2, timestamp: "2023-01-01T00:00:00Z"
}
};
// Accessing the data
const users = apiResponse.data.users;
console.log(users[0].name);  // "Alice"

Do's and Don'ts

  • Use index signatures for collections with dynamic keys
  • Combine with explicit properties for known fields
  • Keep value types specific (avoid any )
  • Use readonly when mutation isn't needed
  • Prefer fixed interfaces when keys are known
  • Forget that all properties must conform to the index signature type
  • Reinvent mapped types-use the dedicated page for transformations

Property Name Conflicts

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<K, T>

Use an index signature for flexible/dynamic keys and when mixing with other properties.

Use Record<K, T> for concise simple mappings.

// Index signature
interface StringMap {
 [key: string]: string;
}
// Record
type StringRecord = Record<string, string>;

Conclusion

Use index signatures when keys are dynamic but value shapes are consistent.

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

Previous

TypeScript Namespaces

Next

TypeScript Declaration Merging

This chapter

Overview
40

Lessons

241m

Read time

1. TypeScript Tutorial2. TypeScript Introduction3. TypeScript Getting Started4. TypeScript Simple Types5. TypeScript Explicit Types and Inference6. TypeScript Special Types7. TypeScript Arrays8. TypeScript Tuples9. TypeScript Object Types10. TypeScript Enums11. TypeScript Type Aliases and Interfaces12. TypeScript Union Types13. TypeScript Functions14. TypeScript Casting15. TypeScript Classes16. TypeScript Basic Generics17. TypeScript Utility Types18. TypeScript Keyof19. TypeScript Null & Undefined20. TypeScript Definitely Typed21. TypeScript 5.x Updates22. TypeScript Configuration23. TypeScript with Node.js24. TypeScript with React25. TypeScript Tooling26. TypeScript Advanced Types27. TypeScript Type Guards28. TypeScript Conditional Types29. TypeScript Mapped Types30. TypeScript Type Inference31. TypeScript Literal Types32. TypeScript Namespaces33. TypeScript Index Signatures34. TypeScript Declaration Merging35. TypeScript Async Programming36. TypeScript Decorators37. TypeScript in JavaScript Projects (JSDoc)38. TypeScript Migration Guide39. TypeScript Error Handling40. TypeScript Best Practices

On this page

Understanding Index Signatures in TypeScriptKey ConceptsString Index SignaturesNumber Index SignaturesMixed Property TypesReadOnly Index SignaturesAPI Response HandlingDo's and Don'tsProperty Name ConflictsIndex Signatures vs. Record<K, T>Conclusion