Typescript Index Signatures And Keyof Operator Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    7 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of TypeScript Index Signatures and keyof Operator

TypeScript Index Signatures and keyof Operator

Syntax:

interface MyDictionary {
  [index: string]: string;  // Any property that is a string can hold a string value
}
  • Here, any property name within objects of type MyDictionary should be a string and its value should also be a string.

Use Case Example:

interface Settings {
  [option: string]: number;  // All properties must be numbers
}

const userSettings: Settings = {
  brightness: 70,
  volume: 50,
};  // This works because both 'brightness' and 'volume' are strings and hold number values

Keyof Operator: The keyof operator is used to extract the property keys from a type. The result of keyof T is a literal string or numeric union of the property names of T. This operator is particularly useful in combination with indexed access types, which allow you to access the type of a specific property in an object.

Syntax:

interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person;  // union type "name" | "age"

Use Case Example:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person: Person = { name: 'John', age: 30 };

const name = getProperty(person, 'name');  // 'name' is a valid key and type of 'name' is string
const age  = getProperty(person, 'age');   // 'age' is a valid key and type of 'age' is number

In the above example, the function getProperty takes an object and a key and returns the property of the object with the provided key. The keyof operator ensures type safety by restricting the key parameter to only be one of the valid keys in the type T.

Combining Index Signatures and keyof Operator: You can leverage both features together to create flexible and type-safe code.

Example:

interface MyObject {
  [key: string]: number;    // index signature
}

type Keys = keyof MyObject;    // 'Keys' is 'string' in this context because of the index signature

function getValue(obj: MyObject, key: Keys) {
  return obj[key];
}

const myObj: MyObject = { a: 1, b: 2, c: 3 };

const value = getValue(myObj, 'a');  // Works fine, returns 1

Here the keyof MyObject evaluates to string since the keys in MyObject are defined via an index signature allowing any string as a key.

General Keywords:

  • Index Signatures
  • keyof Operator
  • TypeScript
  • Types
  • Interfaces
  • Type Safety
  • TypeScript Generics
  • LITERAL Types
  • Indexed Access Type
  • Property Keys
  • Object Types

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement TypeScript Index Signatures and keyof Operator

TypeScript Index Signatures

Index Signatures allow you to define the types of an object's keys and the shape of an object when you don't know the exact keys in advance.

Example 1: Basic Index Signature

Let's create an object that acts as a dictionary where the keys are strings and the values are numbers.

interface NumberDictionary {
  [index: string]: number;
}

const numbers: NumberDictionary = {
  one: 1,
  two: 2,
  three: 3,
};

console.log(numbers["one"]); // Output: 1
console.log(numbers["two"]); // Output: 2

Explanation:

  • We define an interface NumberDictionary with an index signature [index: string]: number; which means all keys must be strings and all values must be numbers.
  • We then create an object numbers that adheres to this interface and log some values.

Example 2: Mixed Key and Value Types

Now, let’s consider an object where keys can be strings and values can be either numbers or strings.

interface StringOrNumberDictionary {
  [index: string]: string | number;
}

const mixedValues: StringOrNumberDictionary = {
  id: 101,
  name: "John Doe",
};

console.log(mixedValues["id"]);   // Output: 101
console.log(mixedValues["name"]); // Output: John Doe

Explanation:

  • The index signature [index: string]: string | number; allows the values to be either strings or numbers.
  • The mixedValues object uses both types of values.

keyof Operator

The keyof operator is used to obtain the union of keys of a certain type. This can be very useful in conjunction with mapped types and generics.

Example 3: Basic Usage of keyof

Let’s use the keyof operator to get the keys of an object.

interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person; // "name" | "age"

const key1: PersonKeys = "name";
const key2: PersonKeys = "age";

console.log(key1); // Output: name
console.log(key2); // Output: age

Explanation:

  • We define an interface Person with keys name (string) and age (number).
  • Using keyof Person, we get the type "name" | "age", which is stored in PersonKeys.
  • Variables key1 and key2 are of type PersonKeys and can only take the values "name" or "age".

Example 4: Using keyof with Mapped Types

Let's create a type that makes all keys of an object readonly.

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

const readonlyPerson: ReadonlyPerson = {
  name: "Alice",
  age: 30,
};

// readonlyPerson.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.

Explanation:

  • We define ReadonlyPerson as a mapped type that iterates over the keys of Person using keyof and sets each value to be readonly.
  • We cannot modify the properties of readonlyPerson because they are readonly.

Example 5: Combination of Index Signatures and keyof

Now, let’s create a function that logs the value of a key from an object if the key exists.

interface User {
  name: string;
  email: string;
}

function logUserValue(user: User, key: keyof User): void {
  console.log(user[key]);
}

const user: User = {
  name: "Bob",
  email: "bob@example.com",
};

logUserValue(user, "name");  // Output: Bob
logUserValue(user, "email"); // Output: bob@example.com
// logUserValue(user, "password"); // Error: Argument of type '"password"' is not assignable to parameter of type 'keyof User'.

Explanation:

  • We define a function logUserValue that takes a User object and a key of type keyof User.
  • This ensures that the key provided can only be "name" or "email".
  • Trying to pass a key that does not exist in User will result in a compile-time error.

Summary

  • Index Signatures: Allow you to define types for dynamic properties in an object.
  • keyof Operator: Fetches the union of keys from a type, useful for type safety when working with object properties.
  • Mapped Types combined with keyof can be used to modify property types based on original types.

Top 10 Interview Questions & Answers on TypeScript Index Signatures and keyof Operator


1. What is an index signature in TypeScript?

Answer: An index signature in TypeScript defines a type where objects can have unknown keys, but all keys must conform to a specific type (like string or number), and all values must conform to another specific type. This is particularly useful when you want to define objects that act as dictionaries or mappings.

Example:

interface Dictionary {
  [key: string]: string;
}

const dict: Dictionary = {
  key1: "value1",
  key2: "value2",
};

Explanation: Here, Dictionary can have any number of keys that are strings, and each key maps to a string value.


2. Can an index signature enforce value types other than string?

Answer: Yes, while index signatures commonly use string or number as the key type, they can enforce any value type that matches the type of actual values in the object.

Example:

interface NumericDictionary {
  [key: number]: string; // Key is a number, value is a string
}

const numDict: NumericDictionary = {
  1: "one",
  2: "two",
};

Explanation: In NumericDictionary, keys must be numbers, and their values can only be strings.


3. How can you use a combination of known and index signatures in TypeScript?

Answer: You can combine known properties with index signatures in an interface or type definition. This allows you to define specific known properties while still allowing for dynamic, indexed properties.

Example:

interface Config {
  version: number;
  [key: string]: any; // Allow any other string-indexed properties
}

const config: Config = {
  version: 1,
  name: "My Config",
  enabled: true,
};

Explanation: Config has a known property version, but it can also have other properties with string keys.


4. What is the keyof operator in TypeScript?

Answer: The keyof operator is a TypeScript utility that returns a union of the type's keys. It's useful for creating type-safe keys and for extracting keys from a type.

Example:

interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person; // "name" | "age"

Explanation: PersonKeys becomes a union type "name" | "age", containing all the possible keys of the Person interface.


5. How can you use keyof with arrays and tuples?

Answer: keyof can also be used with arrays and tuples to retrieve their index types. For arrays, it will return string literals "length", "pop", "push", etc., plus the numeric keys. For tuples, it will return the known numeric indexes and array methods.

Example:

type ArrayKeys = keyof Array<number>; // "length" | "pop" | "push" | ... and numeric keys like "0", "1", etc.
type TupleKeys = keyof [string, number]; // "0" | "1" | "length" | "pop" | ...

Explanation: ArrayKeys includes the array methods and any numeric index, while TupleKeys includes only the existing indexes 0 and 1, plus array methods.


6. How can you use the keyof operator with generics?

Answer: keyof can be combined with generics to create flexible and reusable functions that operate on the keys of various types.

Example:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
const name = getProperty(user, "name"); // "Alice"
const age = getProperty(user, "age"); // 30

Explanation: getProperty is a generic function that takes an object and a key that exists on that object, returning the value at that key. The keyof operator ensures type safety by restricting K to be a key of T.


7. What is the difference between keyof and index signatures?

Answer:

  • keyof Operator: Returns a union type of all keys available in a given type. It is used for type-safe access to properties.
  • Index Signatures: Define dynamic properties that may not be known at the time of writing the code, allowing objects to have properties added or removed as needed. They enforce a type for keys and values.

Example:

interface Example {
  [key: string]: any; // Index signature
  knownProperty: string; // Known property
}

type ExampleKeys = keyof Example; // "knownProperty" | string (because of the index signature)

Explanation: ExampleKeys contains "knownProperty" and the index signature allows any string key, but keyof combines both into a single union type.


8. How can you restrict an index signature to specific keys?

Answer: While index signatures typically allow any key of a specified type (e.g., string or number), they can be restricted to specific keys by combining them with known properties. However, TypeScript doesn't natively support restricting index signatures to a specific set of strings.

Example:

interface SpecificKeys {
  [key: "name" | "age"]: string | number; // Restricted keys
  knownProperty: boolean;
}

const obj: SpecificKeys = {
  name: "John",
  age: 30,
  knownProperty: true,
};

Explanation: SpecificKeys can only have keys "name" and "age", and their values must be either string or number.


9. Can you use the keyof operator with union types?

Answer: When used with union types, keyof returns a union of keys from all types in the union.

Example:

interface Circle {
  radius: number;
}

interface Square {
  side: number;
}

type Shape = Circle | Square;
type ShapeKeys = keyof Shape; // "radius" | "side"

Explanation: ShapeKeys is a union of all possible keys from the Circle and Square interfaces, resulting in "radius" | "side".


10. How can you use keyof with mapped types to create a new type that transforms properties?

Answer: Mapped types allow you to create new types by transforming properties of an existing type, often using keyof to iterate over the keys.

Example:

interface User {
  name: string;
  email: string;
}

type UserWithOptionalFields<T> = {
  [K in keyof T]?: T[K];
};

type OptionalUser = UserWithOptionalFields<User>;
// OptionalUser now has all keys of User as optional properties

Explanation: UserWithOptionalFields is a mapped type that takes a generic type T and turns all its properties optional. keyof T is used to iterate over each key in T, making them optional with the ? modifier.


Conclusion

Understanding index signatures and the keyof operator in TypeScript can help you write more flexible and type-safe code. These features allow you to define objects with dynamic properties, extract and restrict keys, and create generic types based on other types. By leveraging these concepts, you can enhance the robustness and scalability of your TypeScript applications.


You May Like This Related .NET Topic

Login to post a comment.