Web Development
TypeScript
Subjective
Oct 04, 2025
How do you implement a type-safe dependency injection container?
Detailed Explanation
Use generic constraints for service registration, conditional types for dependency resolution, and mapped types for service discovery.
Basic DI Container:
type ServiceMap = {};
type Constructor = new (...args: any[]) => T;
class DIContainer {
private services = new Map();
private singletons = new Map();
register(token: string, implementation: Constructor): void {
this.services.set(token, implementation);
}
registerSingleton(token: string, implementation: Constructor): void {
this.services.set(token, implementation);
this.singletons.set(token, null);
}
resolve(token: string): T {
if (this.singletons.has(token)) {
let instance = this.singletons.get(token);
if (!instance) {
const Implementation = this.services.get(token);
instance = new Implementation();
this.singletons.set(token, instance);
}
return instance;
}
const Implementation = this.services.get(token);
return new Implementation();
}
}
Type-Safe Version:
interface ServiceRegistry {
UserService: UserService;
EmailService: EmailService;
DatabaseService: DatabaseService;
}
type ServiceToken = keyof ServiceRegistry;
class TypeSafeDIContainer {
private services = new Map();
register(
token: K,
implementation: Constructor
): void {
this.services.set(token, implementation);
}
resolve(token: K): ServiceRegistry[K] {
const Implementation = this.services.get(token);
return new Implementation() as ServiceRegistry[K];
}
}
// Usage
const container = new TypeSafeDIContainer();
container.register('UserService', UserServiceImpl);
const userService = container.resolve('UserService'); // Type: UserService
Decorator-Based DI:
const INJECTION_TOKENS = new Map();
function Injectable(token: string) {
return function (target: T) {
return target;
};
}
function Inject(token: string) {
return function (target: any, propertyKey: string | symbol | undefined, parameterIndex: number) {
const existingTokens = INJECTION_TOKENS.get(target) || [];
existingTokens[parameterIndex] = token;
INJECTION_TOKENS.set(target, existingTokens);
};
}
@Injectable('UserService')
class UserService {
constructor(@Inject('DatabaseService') private db: DatabaseService) {}
}
Benefits:
• Compile-time type safety for service resolution
• Prevents injection of wrong service types
• Excellent IDE autocomplete
• Supports decorator-based injection.
Discussion (0)
No comments yet. Be the first to share your thoughts!
Share Your Thoughts