Skip to content

Instantly share code, notes, and snippets.

@greenido
Created November 3, 2024 00:04
Show Gist options
  • Save greenido/43da003aff88da0f05d23c0d07bb1b5b to your computer and use it in GitHub Desktop.
Save greenido/43da003aff88da0f05d23c0d07bb1b5b to your computer and use it in GitHub Desktop.
Dependency Injection Examples in TypeScript
// 1. Interfaces for our dependencies
interface ILogger {
log(message: string): void;
}
interface IEmailService {
sendEmail(to: string, subject: string, content: string): Promise<boolean>;
}
interface IUserRepository {
findById(id: string): Promise<User>;
save(user: User): Promise<void>;
}
// 2. Implementation of our dependencies
class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
class SmtpEmailService implements IEmailService {
constructor(private smtpHost: string, private smtpPort: number) {}
async sendEmail(to: string, subject: string, content: string): Promise<boolean> {
// Implementation for SMTP email sending
console.log(`Sending email to ${to}`);
return true;
}
}
// 3. Domain Models
class User {
constructor(
public id: string,
public email: string,
public name: string
) {}
}
// 4. Constructor Injection Example
class UserService {
constructor(
private readonly userRepository: IUserRepository,
private readonly emailService: IEmailService,
private readonly logger: ILogger
) {}
async updateUserProfile(userId: string, newName: string): Promise<void> {
this.logger.log(`Updating profile for user ${userId}`);
const user = await this.userRepository.findById(userId);
if (!user) {
throw new Error('User not found');
}
user.name = newName;
await this.userRepository.save(user);
await this.emailService.sendEmail(
user.email,
'Profile Updated',
`Hello ${user.name}, your profile has been updated.`
);
}
}
// 5. Property Injection Example
class NotificationService {
// Properties can be injected after instantiation
public logger!: ILogger;
public emailService!: IEmailService;
async sendNotification(user: User, message: string): Promise<void> {
this.logger.log(`Sending notification to ${user.email}`);
await this.emailService.sendEmail(
user.email,
'New Notification',
message
);
}
}
// 6. Method Injection Example
class ReportGenerator {
generateReport(data: any[], logger: ILogger): string {
logger.log('Generating report...');
// Report generation logic
return JSON.stringify(data);
}
}
// 7. Simple DIContainer implementation
class DIContainer {
private services: Map<string, any> = new Map();
register(key: string, implementation: any): void {
this.services.set(key, implementation);
}
resolve<T>(key: string): T {
const service = this.services.get(key);
if (!service) {
throw new Error(`Service ${key} not found in container`);
}
return service as T;
}
}
// 8. Usage Example
// Create a simple DI container
const container = new DIContainer();
// Register services
container.register('logger', new ConsoleLogger());
container.register('emailService', new SmtpEmailService('smtp.example.com', 587));
container.register('userRepository', new class implements IUserRepository {
async findById(id: string): Promise<User> {
return new User(id, '[email protected]', 'Test User');
}
async save(user: User): Promise<void> {
console.log('Saving user:', user);
}
});
// Create service with injected dependencies
const userService = new UserService(
container.resolve('userRepository'),
container.resolve('emailService'),
container.resolve('logger')
);
// 9. Testing Example
class MockEmailService implements IEmailService {
public emailsSent: Array<{to: string, subject: string, content: string}> = [];
async sendEmail(to: string, subject: string, content: string): Promise<boolean> {
this.emailsSent.push({to, subject, content});
return true;
}
}
class MockLogger implements ILogger {
public logs: string[] = [];
log(message: string): void {
this.logs.push(message);
}
}
// Test example
async function testUserService() {
// Arrange
const mockLogger = new MockLogger();
const mockEmailService = new MockEmailService();
const userRepository = container.resolve<IUserRepository>('userRepository');
const testUserService = new UserService(
userRepository,
mockEmailService,
mockLogger
);
// Act
await testUserService.updateUserProfile('123', 'New Name');
// Assert
console.assert(mockLogger.logs.length > 0, 'Logger should have been called');
console.assert(mockEmailService.emailsSent.length === 1, 'Email should have been sent');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment