Type-Safe Reactive Library in TypeScript
Reactive programming allows for building applications that respond to data changes in a declarative way. This challenge asks you to create a simplified, type-safe reactive library in TypeScript, focusing on observable streams and subscription management. Building such a library demonstrates a strong understanding of TypeScript's type system and functional programming principles.
Problem Description
You are tasked with creating a basic reactive library with the following core components:
Observable<T>: A class representing a stream of data of typeT. It should be able to emit values over time.Subscription: A class representing a subscription to an observable. It should provide a method to unsubscribe, stopping the reception of further values.subscribe()method: TheObservableclass must have asubscribe()method that accepts a callback function. This callback should be invoked each time the observable emits a new value. Thesubscribe()method should return aSubscriptionobject.- Type Safety: The library must be type-safe. The type of the values emitted by the observable should be correctly inferred and enforced.
Key Requirements:
- The
Observableclass should maintain a list of subscribers. - When a new value is emitted, the
Observableshould iterate through its subscribers and invoke the callback function for each subscriber with the emitted value. - The
Subscriptionobject should allow subscribers to unsubscribe from the observable, removing themselves from the subscriber list. - Unsubscribing should prevent further notifications to the unsubscribed subscriber.
- The library should handle multiple subscriptions to the same observable.
Expected Behavior:
- Creating an
Observableshould initialize an empty subscriber list. - Calling
subscribe()on anObservableshould return aSubscriptionobject. - Calling
next()on anObservableshould invoke the callback function of each subscriber with the provided value. - Calling
unsubscribe()on aSubscriptionshould remove the subscriber from the observable's subscriber list. - Subscribers that have unsubscribed should not receive further notifications.
Edge Cases to Consider:
- What happens if
subscribe()is called multiple times on the same observable? - What happens if
unsubscribe()is called multiple times on the same subscription? - How should the library handle errors that occur within the subscriber's callback function? (For simplicity, you can ignore error handling in this challenge, but consider it for future improvements.)
- What happens if the observable is disposed of before all subscribers unsubscribe? (Again, for simplicity, you can ignore this for now.)
Examples
Example 1:
Input:
const observable = new Observable<number>();
const subscription1 = observable.subscribe(value => console.log('Subscriber 1:', value));
observable.next(1);
observable.next(2);
subscription1.unsubscribe();
observable.next(3);
Output:
Subscriber 1: 1
Subscriber 1: 2
Explanation:
The observable emits 1 and 2, which are received by subscriber 1. Subscriber 1 unsubscribes, so the emission of 3 is not received.
Example 2:
Input:
const observable = new Observable<string>();
const subscription1 = observable.subscribe(value => console.log('Subscriber 1:', value));
const subscription2 = observable.subscribe(value => console.log('Subscriber 2:', value));
observable.next('hello');
subscription1.unsubscribe();
observable.next('world');
Output:
Subscriber 1: hello
Subscriber 2: hello
Subscriber 2: world
Explanation:
Both subscribers receive 'hello'. Subscriber 1 unsubscribes. Subscriber 2 receives 'world'.
Example 3: (Multiple subscriptions and unsubscribing)
Input:
const observable = new Observable<boolean>();
const subscription1 = observable.subscribe(value => console.log('Sub 1:', value));
const subscription2 = observable.subscribe(value => console.log('Sub 2:', value));
const subscription3 = observable.subscribe(value => console.log('Sub 3:', value));
observable.next(true);
subscription2.unsubscribe();
observable.next(false);
subscription3.unsubscribe();
observable.next(true);
Output:
Sub 1: true
Sub 2: true
Sub 3: true
Sub 1: false
Sub 3: false
Explanation:
All three subscribers initially receive 'true'. Subscription 2 unsubscribes. 'false' is received by Sub 1 and Sub 3. Sub 3 unsubscribes. 'true' is only received by Sub 1.
Constraints
- The code must be written in TypeScript.
- The solution should be relatively concise and easy to understand.
- The
Observableclass should havesubscribe()andnext()methods. - The
Subscriptionclass should have anunsubscribe()method. - The type
Tmust be correctly inferred and used throughout the library. - No external libraries are allowed (e.g., RxJS).
Notes
- Focus on the core functionality of observables and subscriptions. Error handling, disposal, and more advanced features are beyond the scope of this challenge.
- Think about how to manage the subscriber list efficiently.
- Consider how to ensure type safety when emitting values.
- This is a simplified implementation. Real-world reactive libraries are significantly more complex. The goal here is to demonstrate understanding of the fundamental concepts.