DEV Community

Josh Console for EF Go Ahead Tours

Posted on • Edited on

26 3

NestJS Dependency Injection with Abstract Classes

This article was also posted on eftech.com

A seemingly common complaint regarding NestJS and TypeScript is the absence of interfaces in runtime code (since interfaces are removed during transpilation). This limitation prevents the use of constructor-based dependency injection and instead necessitates use of the @Inject decorator. That decorator requires us to associate some "real" JavaScript token with the interface to resolve the dependency - often just a string of the interface name:

/* IAnimal.ts */
export interface IAnimal {
  speak(): string;
}

/* animal.service.ts */
export class Dog implements IAnimal {
  speak() {
    return "Woof";
  }
}

/* animal.module.ts */
@Module({
  providers: [{
    provide: "IAnimal",
    useClass: Dog,
  }]
})
export class AnimalModule

/* client.ts */
export class Client {
  constructor(@Inject("IAnimal") private animal: IAnimal) {}

  // Later...
  this.animal.speak();
}
Enter fullscreen mode Exit fullscreen mode

This approach is adequate, but the "IAnimal" magic string is a little fragile and not actually associated with the interface. A modest improvement can be made by exporting a token with the interface:

/* IAnimal.ts */
export interface IAnimal {
  speak(): string;
}

export const IAnimalToken = "IAnimal";
// or slightly better...
export const IAnimalToken = Symbol("IAnimal");

/* animal.module.ts */
@Module({
  providers: [{
    provide: IAnimalToken,
    useClass: Dog,
  }]
})
export class AnimalModule

/* client.ts */
export class Client {
  constructor(@Inject(IAnimalToken) private animal: IAnimal) {}

  // Later...
  this.animal.speak();
}
Enter fullscreen mode Exit fullscreen mode

At least now the token reference is consistent. Still, manually injecting tokens like this can be cumbersome and conceptually unsatisfying. What we would really like is a true JavaScript object in the place of the interface, which we can reference universally at runtime. What we would really like is...an abstract class!

/* IAnimal.ts */
export abstract class IAnimal {
    abstract speak(): string;
}

/* animal.service.ts */
// You can implement an abstract class without extending it!
export class Dog implements IAnimal {
  speak() {
    return "Woof";
  }
}

/* animal.module.ts */
@Module({
  providers: [{
    provide: IAnimal,
    useClass: Dog,
  }]
})
export class AnimalModule

/* client.ts */
export class Client {
  constructor(private animal: IAnimal) {}

  // Later...
  this.animal.speak();
}
Enter fullscreen mode Exit fullscreen mode

Now a single, "real" JavaScript reference is used throughout the application to resolve the dependency. Whether or not the minor inconvenience of token-based dependency injection justifies this usage of abstract classes is a separate question. If you're feeling lazy, though, this is a convenient option!

Heroku

Deliver your unique apps, your own way.

Heroku tackles the toil — patching and upgrading, 24/7 ops and security, build systems, failovers, and more. Stay focused on building great data-driven applications.

Learn More

Top comments (3)

Collapse
 
bencun profile image
bencun

This is super super super useful. I had no idea that we were able to implement abstract classes instead of extending them. Thank you very much for sharing!

Collapse
 
samandarjumanov profile image
Samandar Jumanov

Great

Collapse
 
kspshnik profile image
Evgeny Karpel

And what if I need several animals?

Image of Quadratic

Free AI chart generator

Upload data, describe your vision, and get Python-powered, AI-generated charts instantly.

Try Quadratic free

👋 Kindness is contagious

Dive into this informative piece, backed by our vibrant DEV Community

Whether you’re a novice or a pro, your perspective enriches our collective insight.

A simple “thank you” can lift someone’s spirits—share your gratitude in the comments!

On DEV, the power of shared knowledge paves a smoother path and tightens our community ties. Found value here? A quick thanks to the author makes a big impact.

Okay