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:
addDependency(watcher: Watcher): Adds aWatcherobject to the collection. AWatcheris simply an object with anidproperty (string).removeDependency(watcherId: string): Removes aWatcherfrom the collection based on itsid.getDependencies(): Watcher[]: Returns a copy of the array ofWatcherobjects currently in the collection.hasDependency(watcherId: string): boolean: Checks if aWatcherwith the givenidexists 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
Watcherobjects are immutable after creation. TheDependencyCollectionshould not modify theWatcherobjects 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
removeDependencymethod should handle the case where thewatcherIddoes not exist gracefully (no error should be thrown; the collection should remain unchanged). - The time complexity of
addDependencyandremoveDependencyshould be O(1) on average.getDependenciesshould be O(n) where n is the number of dependencies. - The
watcherIdwill 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.