Injectable & DI Basics — Series 3

Preview — 3 of 10 questions

A calls bump() twice, then B calls it once. What does B get?

javascript
@Injectable() export class CountersService { private n = 0; bump() { return ++this.n; } }

@Injectable() export class A { constructor(readonly counters: CountersService) {} }
@Injectable() export class B { constructor(readonly counters: CountersService) {} }
A1 — each consumer receives its own instance
B3 — both injected the same default-scoped instance, so the counter is shared
C0, because the field is reset when a second consumer injects the provider
Dundefined, since two classes may not inject the same provider

What does the decorator change?

javascript
@Injectable()
export class ReportService {
  constructor(@Optional() private readonly tracer?: TracerService) {}

  run() {
    this.tracer?.start('report');
  }
}
AIt defers resolution until the first time tracer is accessed
BIt creates a no-op implementation when the real provider is missing
CIt makes the dependency transient, so a fresh tracer is built per call
DIf TracerService is not resolvable, Nest injects undefined instead of failing to start — turning a hard dependency into an opt-in one, which is why the call site uses ?.

What does form (2) gain?

javascript
// (1)
export class OrdersService {
  private readonly payments = new PaymentsService(new HttpClient(), new ConfigReader());
}
// (2)
export class OrdersService {
  constructor(private readonly payments: PaymentsService) {}
}
AThe dependency is supplied from outside, so a test can pass a double and the implementation can be swapped centrally — while form (1) hard-codes both the class and its entire construction chain into every consumer
BIt is faster, because the container caches constructor calls
CIt avoids a TypeScript error, since new is not allowed in a field initialiser
DIt makes OrdersService immutable

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.