Hone logo
Hone
Problems

Mutable Helper for State Management in TypeScript

This challenge focuses on creating a reusable and flexible "mutable helper" utility in TypeScript. Such helpers are invaluable for managing and updating complex state within applications, especially in scenarios where direct mutation is undesirable or when you need to apply transformations consistently. You will build a function that allows for controlled, mutable updates to any object or array.

Problem Description

Your task is to implement a TypeScript function named mutableHelper. This function should accept an object or array and a "mutator" function as arguments. The mutableHelper should return a new, modified version of the input object or array, based on the transformations defined in the mutator function. The original input should remain unchanged.

Key Requirements:

  1. Immutability: The mutableHelper must not modify the original input object or array. It should always return a new instance.
  2. Mutator Function: The mutator function will receive the current state (a copy of the input) and should return the modified state. This function can perform any necessary updates.
  3. Type Safety: The function should be generic to handle any type of object or array.

Expected Behavior:

When mutableHelper is called with an initial state and a mutator function, it should:

  • Create a deep copy of the input state.
  • Pass this deep copy to the mutator function.
  • Return the result of the mutator function.

Edge Cases:

  • Empty Objects/Arrays: The helper should correctly handle empty objects and arrays.
  • Nested Structures: The deep copy mechanism should handle nested objects and arrays correctly.

Examples

Example 1:

Input:
initialState = { name: "Alice", age: 30 }
mutator = (state) => {
  state.age += 1;
  return state;
}

Output:
{ name: "Alice", age: 31 }

Explanation:
The initial state is { name: "Alice", age: 30 }. The mutator increments the age. The mutableHelper returns a new object with the updated age, while the original initialState remains { name: "Alice", age: 30 }.

Example 2:

Input:
initialState = [1, 2, 3]
mutator = (state) => {
  state.push(4);
  return state;
}

Output:
[1, 2, 3, 4]

Explanation:
The initial state is the array [1, 2, 3]. The mutator pushes the number 4 to the end. The mutableHelper returns a new array [1, 2, 3, 4]. The original array remains [1, 2, 3].

Example 3:

Input:
initialState = { user: { id: 1, profile: { name: "Bob" } } }
mutator = (state) => {
  state.user.profile.name = "Robert";
  return state;
}

Output:
{ user: { id: 1, profile: { name: "Robert" } } }

Explanation:
This example demonstrates nested object mutation. The mutator updates a deeply nested property. The mutableHelper ensures a deep copy, so the original nested structure is preserved.

Constraints

  • The mutableHelper function should be efficient enough for typical application state management. Avoid overly complex or performance-intensive deep cloning if simpler methods suffice.
  • Input initialState can be any valid JavaScript object or array.
  • The mutator function will always receive a single argument (the copied state) and must return the modified state.

Notes

Consider how to achieve a reliable "deep copy" of JavaScript objects and arrays. Standard assignment (=) and spread syntax (...) create shallow copies, which might not be sufficient for nested structures. You might need to implement a custom deep cloning logic or leverage existing libraries (though for this challenge, implementing your own is encouraged to understand the mechanics). Think about how to handle different data types within your deep copy (e.g., Dates, RegExps, etc.) if aiming for a truly robust solution, although basic object/array cloning is the primary goal.

Loading editor...
typescript