260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Use Injection Tokens for Interfaces
|
||||
impact: HIGH
|
||||
impactDescription: Enables interface-based DI at runtime
|
||||
tags: dependency-injection, tokens, interfaces
|
||||
---
|
||||
|
||||
## Use Injection Tokens for Interfaces
|
||||
|
||||
TypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.
|
||||
|
||||
**Incorrect (interface can't be used as token):**
|
||||
|
||||
```typescript
|
||||
// Interface can't be used as injection token
|
||||
interface PaymentGateway {
|
||||
charge(amount: number): Promise<PaymentResult>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StripeService implements PaymentGateway {
|
||||
charge(amount: number) { /* ... */ }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
// This WON'T work - PaymentGateway doesn't exist at runtime
|
||||
constructor(private payment: PaymentGateway) {}
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (symbol tokens or abstract classes):**
|
||||
|
||||
```typescript
|
||||
// Option 1: String/Symbol tokens (most flexible)
|
||||
export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');
|
||||
|
||||
export interface PaymentGateway {
|
||||
charge(amount: number): Promise<PaymentResult>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StripeService implements PaymentGateway {
|
||||
async charge(amount: number): Promise<PaymentResult> {
|
||||
// Stripe implementation
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class MockPaymentService implements PaymentGateway {
|
||||
async charge(amount: number): Promise<PaymentResult> {
|
||||
return { success: true, id: 'mock-id' };
|
||||
}
|
||||
}
|
||||
|
||||
// Module registration
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: PAYMENT_GATEWAY,
|
||||
useClass: process.env.NODE_ENV === 'test'
|
||||
? MockPaymentService
|
||||
: StripeService,
|
||||
},
|
||||
],
|
||||
exports: [PAYMENT_GATEWAY],
|
||||
})
|
||||
export class PaymentModule {}
|
||||
|
||||
// Injection
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
constructor(
|
||||
@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway,
|
||||
) {}
|
||||
|
||||
async createOrder(dto: CreateOrderDto) {
|
||||
await this.payment.charge(dto.amount);
|
||||
}
|
||||
}
|
||||
|
||||
// Option 2: Abstract class (carries runtime type info)
|
||||
export abstract class PaymentGateway {
|
||||
abstract charge(amount: number): Promise<PaymentResult>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StripeService extends PaymentGateway {
|
||||
async charge(amount: number): Promise<PaymentResult> {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
|
||||
// No @Inject needed with abstract class
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
constructor(private payment: PaymentGateway) {}
|
||||
}
|
||||
```
|
||||
|
||||
Reference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)
|
||||
Reference in New Issue
Block a user