- TypeScript Tutorial
- TypeScript Home
- TypeScript Introduction
- TypeScript Setup
- TypeScript First Program
- TypeScript vs JavaScript
- TypeScript Data Types
- TypeScript Type Inference
- TypeScript Type Annotations
- TypeScript Interfaces
- TypeScript Enums
- TypeScript Type Aliases
- TypeScript Type Assertions
- TypeScript Variables
- TypeScript Functions
- TypeScript Functions
- TypeScript Optional Parameters
- TypeScript Default Parameters
- TypeScript Rest Parameters
- TypeScript Arrow Functions
- Classes and Objects
- Introduction to Classes
- Properties and Methods
- Access Modifiers
- Static Members
- Inheritance
- Abstract Classes
- Interfaces vs Classes
- Advanced Types
- TypeScript Union Types
- TypeScript Intersection Types
- TypeScript Literal Types
- TypeScript Nullable Types
- TypeScript Type Guards
- TypeScript Discriminated Unions
- TypeScript Index Signatures
- TypeScript Generics
- Introduction to Generics
- TypeScript Generic Functions
- TypeScript Generic Classes
- TypeScript Generic Constraints
- TypeScript Modules
- Introduction to Modules
- TypeScript Import and Export
- TypeScript Default Exports
- TypeScript Namespace
- Decorators
- Introduction to Decorators
- TypeScript Class Decorators
- TypeScript Method Decorators
- TypeScript Property Decorators
- TypeScript Parameter Decorators
- Configuration
- TypeScript tsconfig.json File
- TypeScript Compiler Options
- TypeScript Strict Mode
- TypeScript Watch Mode
TypeScript Index Signatures
In TypeScript, we usually define our object structures by listing every property name and its type. However, there are times when you don't know the exact names of the properties beforehand such as when you're handling a list of user-generated settings, a cache of data from an API, or a simple dictionary of terms. This is where index signatures come in.
Index signatures allow you to define a "catch-all" for properties that haven't been explicitly named. They provide a way to describe the "shape" of an object's keys and the types of their corresponding values, ensuring your code remains type-safe even when dealing with dynamic data.
What is an Index Signature?
An index signature is a special syntax in TypeScript used to represent objects where the property names are unknown at compile time. It acts as a placeholder for any property that might be added to the object later.
You define an index signature using square brackets []. Inside the brackets, you specify a name for the key (this is just a label for readability, usually key or index) and its type (which must be string, number, symbol, or a template literal). Outside the brackets, you specify the type of the value that will be stored under that key.
Syntax
The basic syntax for an index signature looks like this:
interface SomeObject {
[key: string]: type;
}
[key: string]: This tells TypeScript that this object can have any number of properties, and those properties must be strings.type: This represents the data type that every one of those properties must adhere to.
boolean or object as the key type in an index signature. TypeScript only allows string, number, symbol, and template string patterns for keys.
Example of Index Signatures
Imagine you are building a language translation system where you map English words to their Spanish equivalents. Since you could have thousands of words, you wouldn't want to define every single one in an interface. Instead, you use an index signature:
interface Dictionary {
[key: string]: string;
}
const translations: Dictionary = {
hello: "hola",
goodbye: "adiós",
thankYou: "gracias",
};
console.log(translations.hello); // Output: hola
console.log(translations.thankYou); // Output: gracias
Explanation:
- The
Dictionaryinterface says: "You can add any property you want, as long as the property name is a string and the value is also a string." - If you tried to assign a number to
translations.hello, TypeScript would throw a compilation error, protecting you from accidental type mismatches.
Record<string, string> as a cleaner alternative to a full interface.
Key Concepts of Index Signatures
- Flexible Object Types: They are perfect for "bag of properties" objects where the specific keys are determined by external data (like a JSON response).
- Key Types: While
stringis most common,numberis used for "Array-like" objects. In JavaScript, all object keys are technically converted to strings, but TypeScript uses thenumberkey type to enforce numeric indexing. - Exhaustiveness: Once an index signature is defined, every other explicitly named property in that interface must match the index signature's value type.
Example with number Keys
You might use a numeric index signature when you're building a custom data structure that mimics an array or a list where items are accessed by their position:
interface ResponseList {
[index: number]: string;
}
const list: ResponseList = ["Success", "Error", "Pending"];
console.log(list[0]); // Output: Success
console.log(list[1]); // Output: Error
Explanation:
- The
ResponseListinterface ensures that any numeric key used to access the object will return a string. - This is particularly useful when dealing with
argumentsobjects or DOM collections that behave like arrays but aren't actually arrays.
Combining Index Signatures with Other Properties
You can mix specific, known properties with an index signature. This is very common in configuration objects where you have a few "required" settings but want to allow users to add their own custom metadata.
interface Product {
id: number;
name: string;
[key: string]: string | number; // Catch-all for extra metadata
}
const product: Product = {
id: 101,
name: "Mechanical Keyboard",
brand: "Logitech", // Dynamic key
switches: "Brown", // Dynamic key
stock: 50 // Dynamic key
};
Explanation:
- The
Productinterface requires anid(number) and aname(string). - The index signature
[key: string]: string | numberallows any other properties, provided they are either strings or numbers.
[key: string]: string, you cannot have a specific property like id: number. TypeScript will complain because id is a string-based key, and its value (number) violates the index signature (string).
Limitations of Index Signatures
- Value Type Uniformity: As mentioned, all named properties must be subtypes of the index signature's value type. If your index signature is
string, you can't have anumberproperty. - Runtime Errors: TypeScript assumes that if you access an index, the value will exist. This can lead to bugs if the key isn't actually present in the object.
undefined to the value type: [key: string]: string | undefined;. This forces you to check if the property exists before using it.
Example of Restricting Index Signatures
In some cases, you want to use an index signature to ensure that an object only contains a certain type of data, preventing any "polluted" properties from entering your logic.
interface UserPermissions {
[role: string]: boolean;
}
const permissions: UserPermissions = {
admin: true,
editor: false,
viewer: true,
// guest: "yes" // Error: Type 'string' is not assignable to type 'boolean'.
};
Explanation:
- By setting the value type to
boolean, we ensure that thepermissionsobject can only store true/false values. - This is a great way to create a "Set-like" object where you check for the existence or status of specific roles.
Summary
Index signatures are a powerful tool for managing dynamic data in TypeScript. They bridge the gap between strict typing and the flexible, dynamic nature of JavaScript objects.
- Index Signature: Use
[key: T]: Vto handle unknown property names while maintaining type safety. - Consistency: Remember that named properties must be compatible with the index signature's value type.
- Flexibility: You can use
stringornumberas keys to model dictionaries, caches, or array-like structures.
Whether you're building a complex state management system or a simple lookup table, index signatures help you write cleaner, more predictable code that scales with your data.