NestJS: How to pass a service from a child to a parent?

Viewed 1013

I wish to pass an injected microservice through the super(); call to parent classes.

Abstract top-level parent class:

export abstract class Crypto {
  constructor() {
  }
}

2 child classes:

export class BTC extends Crypto {
  constructor(
    @Inject('COIN_API_SERVICE')
    private readonly apiService: ClientProxy, // this is an external microservice
  ) {
    super();
  }
}

export class ETH extends Crypto {

  constructor(
    @Inject('COIN_API_SERVICE')
    private readonly apiService: ClientProxy,
  ) {
    super();
  }
}

And a child class of a child class:

export class ERC20Token extends ETH {

  constructor(
    @Inject('COIN_API_SERVICE')
    private readonly apiService: ClientProxy,
  ) {
    super();
  }
}

Since all ERC20 classes work exactly like ETH instances with a few tweaks, it makes sense to extend it from ETH. However, trying to compile this code ends up in the follow error:

TS2415: Class 'ERC20Token' incorrectly extends base class 'ETH'.   Types have separate declarations of a private property 'apiService'.

But I need to make both instances of ETH and ERC20 Token, so both need to have an apiService. How to resolve this issue?

3 Answers

An option would be to use a factory such that the factory is injected with all the dependencies. Consequently, the objects that are created through the factory.

The constructor of the factory would look somewhat like this:

constructor(
    @Inject('COIN_API_SERVICE')
    private readonly apiService: ClientProxy,
    coinType: CoinType,
) {
}

And then you can instantiate the coins using the injected API service and coin type.

If you only want to make the child constructor compile, you can have a look at the optional decorator: https://docs.nestjs.com/providers#optional-providers

Make the origin class's apiService as protected:

export class ETH extends Crypto {

  constructor(
    @Inject('COIN_API_SERVICE')
    protected readonly apiService: ClientProxy,
  ) {
    super();
  }
}

And pass the service instance in super from child:

export class ERC20Token extends ETH {

  constructor(
    @Inject('COIN_API_SERVICE')
    protected readonly apiService: ClientProxy,
  ) {
    super(apiService);
  }
}

Solution without using super() with better way:

class Parent {
    @Inject('COIN_API_SERVICE')
    protected readonly apiService: ClientProxy;

    // you can inject not only ClientProxy
    // but you should define it into provider:
    // { provide: SomeService.name, useClass: SomeService }
    @Inject(SomeService.name)
    protected readonly someService: SomeService;
}

class Child extends Parent {
    // ...child logic
}
Related