Nest: Add @Optional() decorator

Created on 6 Jul 2018  路  9Comments  路  Source: nestjs/nest

I'm submitting a...

[x ] Feature request

Current behavior

I can't write a class constructor of a class that will be injected where only some of the parameters are understood by nest's injection system.

constructor(@Inject('ConfigService') configService: ConfigService, public readonly s3: S3 = new S3())

If I do, I get an error like

Nest can't resolve dependencies of the S3Service (+, ?). Please make sure that the argument at index [1] is available in the current context.

Expected behavior

constructor(@Inject('ConfigService') configService: ConfigService, @NoInject() public readonly s3: S3 = new S3())

What is the motivation / use case for changing the behavior?

It's nice to be able to write wrapper class signatures using constructor access modifiers, it would be great if it could co-exist with nest's DI

Environment


Nest version: 5.1.0

core done 馃憦 type

Most helpful comment

5.2.0 is here!

All 9 comments

Actually, why don't you create a custom provider?

{ 
    provide: 'S3',
    useValue: new S3(),
}

@kamilmysliwiec This might be a good solution most of the time, but to me this looks like over-complicating things =/

@autaut03 can you elaborate on why this makes it overcomplicated in your use case? Adding just a constant like this for example:

const S3Provider = { 
    provide: 'S3',
    useValue: new S3(),
}

... for each class does not overcomplicate things, does it?

@BrunnerLivio Disclaimer: I'm new to Nest and TS, so please correct me if I'm wrong:

Well, constructor(public readonly s3: S3 = new S3()) should be the same as

readonly s3: S3;

constructor(s3?: S3) {
    this.s3 = s3 || new S3();
}

In your example it's

```typescript
readonly s3: S3;

constructor(s3: S3) {
this.s3 = s3;
}
````

where Module is responsible for creating an instance of S3. To me this looks kind of wierd.

Also, again, correct me if I'm wrong, but Nest's deps are all singletons, meaning your example's behaviour is not the same as author's.

I ended up using a factory based custom provider for better control and injection support like @kamilmysliwiec suggested . It's working, it avoids unnecessary wrappers, it's safe vs refactor, it's fine for my case.

The flexibility for the injector to ignore some constructor params might still be nice in the future, but I haven't _really_ needed it yet.

// S3Factory.ts
import { S3 } from 'aws-sdk';
import { ConfigService } from './ConfigService';

let instance: S3;

export const s3Factory = {
  provide: S3.name,
  useFactory: (configService: ConfigService) => {
    if (!instance) {
      instance = new S3({
        // configure the S3 service using config from ConfigService
      };
    }
    return instance;
  },
  inject: [ConfigService]
};

```typescript
// CommonModule.ts
@Global()
@Module({
providers: [configServiceFactory, winstonLoggerServiceFactory, iotDataFactory, s3Factory],
exports: [configServiceFactory, winstonLoggerServiceFactory, iotDataFactory, s3Factory]
})
export class CommonModule {}

```typescript
// Repository.ts
@Injectable()
export class Repository {
  constructor(@Inject(S3.name) private readonly s3: S3) {}
  // ...
}

Yes I think factories is the way to go. You loose the power of the injection pattern if you allow to not-inject dependencies. I think something like @NoInject should not be allowed, because people tend to use a anti-pattern in the end.

Angular has proven most problem can be solved with this injection pattern, otherwise they would have probably changed their architecture.

5.2.0 is here!

here's a link to the @Optional docs

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

VRspace4 picture VRspace4  路  3Comments

FranciZ picture FranciZ  路  3Comments

menme95 picture menme95  路  3Comments

cojack picture cojack  路  3Comments

anyx picture anyx  路  3Comments