Hone logo
Hone
Problems

Reactive Dependency Collection in Vue (TypeScript)

This challenge focuses on building a simplified dependency collection system, similar to what Vue.js uses internally to track reactive dependencies. Understanding this mechanism is crucial for grasping how Vue's reactivity works and for building custom reactive systems. You'll create a class that tracks which "watchers" are dependent on a given reactive value.

Problem Description

You are tasked with creating a DependencyCollection class in TypeScript. This class will manage a collection of "watchers" that depend on a single reactive value. The class should provide methods to:

  1. addDependency(watcher: Watcher): Adds a Watcher object to the collection. A Watcher is simply an object with an id property (string).
  2. removeDependency(watcherId: string): Removes a Watcher from the collection based on its id.
  3. getDependencies(): Watcher[]: Returns a copy of the array of Watcher objects currently in the collection.
  4. hasDependency(watcherId: string): boolean: Checks if a Watcher with the given id exists in the collection.

The DependencyCollection should internally store the dependencies in an array. Ensure that the getDependencies() method returns a copy of the array to prevent external modification of the internal state.

Watcher Interface:

interface Watcher {
  id: string;
}

Examples

Example 1:

Input:
Initial DependencyCollection: Empty
addDependency({ id: 'watcher1' })
addDependency({ id: 'watcher2' })
getDependencies()
removeDependency('watcher1')
getDependencies()

Output:
[ { id: 'watcher1' } ]
[ { id: 'watcher2' } ]

Explanation:
The first `getDependencies()` call returns the two watchers added. Removing 'watcher1' leaves only 'watcher2'.

Example 2:

Input:
Initial DependencyCollection: Contains [{ id: 'watcherA' }, { id: 'watcherB' }]
addDependency({ id: 'watcherC' })
hasDependency('watcherB')
hasDependency('watcherD')

Output:
[ { id: 'watcherA' }, { id: 'watcherB' }, { id: 'watcherC' } ]
true
false

Explanation:
'watcherC' is added to the existing collection.  'watcherB' exists, but 'watcherD' does not.

Example 3: (Edge Case - Removing a non-existent dependency)

Input:
Initial DependencyCollection: Contains [{ id: 'watcherX' }]
removeDependency('watcherY')
getDependencies()

Output:
[ { id: 'watcherX' } ]

Explanation:
Attempting to remove a non-existent dependency ('watcherY') should not affect the existing dependencies.

Constraints

  • The Watcher objects are immutable after creation. The DependencyCollection should not modify the Watcher objects themselves.
  • The getDependencies() method must return a copy of the internal array. Returning the original array would allow external code to directly modify the collection's state, violating encapsulation.
  • The removeDependency method should handle the case where the watcherId does not exist gracefully (no error should be thrown; the collection should remain unchanged).
  • The time complexity of addDependency and removeDependency should be O(1) on average. getDependencies should be O(n) where n is the number of dependencies.
  • The watcherId will always be a string.

Notes

  • Consider using a simple array to store the dependencies for this exercise. More advanced implementations might use a Map for faster lookups, but that's not required here.
  • Focus on the core logic of dependency tracking and management. Error handling beyond the "non-existent dependency" case is not required.
  • Think about how to ensure that the getDependencies() method returns a new array instance, preventing external modification of the internal state. The spread operator (...) is a common way to achieve this.
  • This is a simplified model. Real-world reactivity systems are significantly more complex, involving dependency tracking across multiple levels of nesting and handling asynchronous updates.
Loading editor...
typescript