Hone logo
Hone
Problems

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:

  1. Observable<T>: A class representing a stream of data of type T. It should be able to emit values over time.
  2. Subscription: A class representing a subscription to an observable. It should provide a method to unsubscribe, stopping the reception of further values.
  3. subscribe() method: The Observable class must have a subscribe() method that accepts a callback function. This callback should be invoked each time the observable emits a new value. The subscribe() method should return a Subscription object.
  4. 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 Observable class should maintain a list of subscribers.
  • When a new value is emitted, the Observable should iterate through its subscribers and invoke the callback function for each subscriber with the emitted value.
  • The Subscription object 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 Observable should initialize an empty subscriber list.
  • Calling subscribe() on an Observable should return a Subscription object.
  • Calling next() on an Observable should invoke the callback function of each subscriber with the provided value.
  • Calling unsubscribe() on a Subscription should 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 Observable class should have subscribe() and next() methods.
  • The Subscription class should have an unsubscribe() method.
  • The type T must 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.
Loading editor...
typescript