bugl
bugl
HomeLearnPatternsSearch
HomeLearnPatternsSearch

Loading lesson path

Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript Index Signatures

Concept visual

TypeScript Index Signatures

keys map to buckets01kiwi:12pear:43apple:7grape:9

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

Formula

: Catch type - related errors at compile time

Basic Index Signatures

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.

Advanced Index Signature Patterns

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

Formula

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.

Real-World Examples

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;
}

Formula

// 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: {

Formula

page: 1, total: 2, timestamp: "2023 - 01 - 01T00:00:00Z"
}
};
// Accessing the data const users = apiResponse.data.users;
console.log(users[0].name);  // "Alice"

Best Practices

Do's and Don'ts

Do:

Use index signatures for collections with dynamic keys

Combine with explicit properties for known fields

Keep value types specific (avoid any )

Previous

TypeScript Namespaces

Next

TypeScript Declaration Merging