Hone logo
Hone
Problems

Angular Module Injector Implementation

This challenge asks you to implement a simplified module injector for Angular, mimicking the core functionality of Angular's dependency injection system. Building a module injector is a fundamental exercise in understanding how Angular manages dependencies and promotes modularity, providing a deeper understanding of the framework's inner workings.

Problem Description

You are tasked with creating a basic module injector that can register and resolve dependencies within a given module. The injector should be able to register services (classes) and resolve them when requested. The injector should support both class-based and function-based providers.

What needs to be achieved:

  • Create a ModuleInjector class.
  • Implement a register method that allows registering services with the injector. This method should accept a service identifier (string) and either a class constructor or a factory function.
  • Implement a resolve method that retrieves a registered service by its identifier. If the service is a class, the injector should create an instance of the class using its constructor. If it's a factory function, the injector should execute the function and return its result.
  • Handle cases where a service is not registered.

Key Requirements:

  • The ModuleInjector should be able to handle both class-based and function-based providers.
  • When resolving a class-based service, the injector should create a new instance of the class.
  • The resolve method should throw an error if the requested service is not registered.
  • The injector should not modify the registered providers.

Expected Behavior:

  • register should successfully register services.
  • resolve should return an instance of the registered class or the result of the factory function.
  • Attempting to resolve an unregistered service should throw an error.

Edge Cases to Consider:

  • What happens if the class constructor requires dependencies that are not registered in the injector? (For simplicity, assume that dependencies are not required for this challenge. The constructor should be invoked without any arguments.)
  • What happens if the factory function throws an error? (The error should be propagated.)
  • What happens if the same service is registered multiple times? (The last registration should take precedence.)

Examples

Example 1:

Input:
injector = new ModuleInjector();
injector.register('logger', Logger);
const logger = injector.resolve('logger');

class Logger {
  log(message: string) {
    console.log(message);
  }
}
Output:
logger.log("Test message"); // Output: "Test message" in console
Explanation: The 'logger' service (Logger class) is registered and then resolved.  The resolved service is an instance of the Logger class.

Example 2:

Input:
injector = new ModuleInjector();
injector.register('apiService', () => new ApiService());

class ApiService {
  getData() {
    return "Data from API";
  }
}

const apiService = injector.resolve('apiService');
Output:
apiService.getData(); // Output: "Data from API"
Explanation: The 'apiService' is registered as a factory function. The factory function creates an instance of ApiService, which is then returned by the resolve method.

Example 3: (Edge Case - Unregistered Service)

Input:
injector = new ModuleInjector();
injector.register('logger', Logger);
try {
  injector.resolve('nonExistentService');
} catch (error) {
  console.error(error.message);
}

class Logger {
  log(message: string) {
    console.log(message);
  }
}
Output:
console.error("Service 'nonExistentService' not found");
Explanation: Attempting to resolve a service that hasn't been registered throws an error.

Constraints

  • The ModuleInjector class should be implemented in TypeScript.
  • The service identifier must be a string.
  • The registered class must be a valid TypeScript class.
  • The factory function must be a function that returns an object.
  • The injector should not use any external libraries beyond standard TypeScript.
  • Performance is not a primary concern for this simplified implementation.

Notes

  • This is a simplified module injector and does not include all the features of Angular's dependency injection system (e.g., providers, multi-providers, hierarchical injectors).
  • Focus on the core functionality of registering and resolving services.
  • Consider using a simple JavaScript object (or Map) to store the registered services.
  • Think about how to handle errors gracefully.
  • The goal is to demonstrate an understanding of the basic principles of dependency injection.
Loading editor...
typescript