Skip to content

Commit

Permalink
Use default class property for @optional injected properties (#1467)
Browse files Browse the repository at this point in the history
Currently the @optional tag overrides class properties defined with typescript-style default values:
```typescript
@Injectable()
class Ninja {
    @Inject(Weapon) @optional() weapon: Weapon = new Katana();

    ....
}
```
this currently sets the weapon property to `undefined` if no Weapon is present in the container. My proposed change allows does not override the default and results in the weapon property being set to new Katana().

(Default values in the constructor already work, but I would prefer not to add boilerplate constructors if this solution is possible.)

Co-authored-by: Podaru Dragos <podaru.dragos@gmail.com>
  • Loading branch information
haaijman-imagem and PodaruDragos committed Oct 17, 2023
1 parent 758a6bd commit eec4159
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

### Fixed
property injection tagged as @optional no longer overrides default values with `undefined`.

## [6.0.2]

Expand Down
4 changes: 3 additions & 1 deletion src/resolution/instantiation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ function createInstanceWithInjections<T>(
args.propertyRequests.forEach((r: interfaces.Request, index: number) => {
const property = r.target.identifier;
const injection = args.propertyInjections[index];
(instance as Record<string | symbol, unknown>)[property] = injection;
if (!r.target.isOptional() || injection !== undefined) {
(instance as Record<string | symbol, unknown>)[property] = injection;
}
});
return instance
}
Expand Down
45 changes: 45 additions & 0 deletions test/annotation/optional.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,49 @@ describe('@optional', () => {

});

it("Should allow to set a default value for class property dependencies flagged as optional", () => {
@injectable()
class Katana {
public name: string;
public constructor() {
this.name = 'Katana';
}
}

@injectable()
class Shuriken {
public name: string;
public constructor() {
this.name = 'Shuriken';
}
}

@injectable()
class Ninja {
public name: string = "Ninja";
@inject("Katana") public katana?: Katana;
@inject("Shuriken") @optional() public shuriken: Shuriken = {
name: "DefaultShuriken",
};
}

const container = new Container();

container.bind<Katana>('Katana').to(Katana);
container.bind<Ninja>('Ninja').to(Ninja);

let ninja = container.get<Ninja>('Ninja');
expect(ninja.name).to.eql('Ninja');
expect(ninja.katana?.name).to.eql('Katana');
expect(ninja.shuriken.name).to.eql('DefaultShuriken');

container.bind<Shuriken>('Shuriken').to(Shuriken);

ninja = container.get<Ninja>('Ninja');
expect(ninja.name).to.eql('Ninja');
expect(ninja.katana?.name).to.eql('Katana');
expect(ninja.shuriken.name).to.eql('Shuriken');
}
);

});
10 changes: 10 additions & 0 deletions wiki/optional_dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,13 @@ class Ninja {
}
}
```

Or using properties injection:
```ts
@injectable()
class Ninja {
public name = "Ninja";
@inject("Katana") public katana: Katana;
@inject("Shuriken") @optional() public shuriken: Shuriken = { name: "DefaultShuriken" } // Default value!
}
```

0 comments on commit eec4159

Please sign in to comment.