Skip to content

Commit eec4159

Browse files
Use default class property for @optional injected properties (#1467)
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 <[email protected]>
1 parent 758a6bd commit eec4159

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
### Changed
1212

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

1516
## [6.0.2]
1617

Diff for: src/resolution/instantiation.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ function createInstanceWithInjections<T>(
6868
args.propertyRequests.forEach((r: interfaces.Request, index: number) => {
6969
const property = r.target.identifier;
7070
const injection = args.propertyInjections[index];
71-
(instance as Record<string | symbol, unknown>)[property] = injection;
71+
if (!r.target.isOptional() || injection !== undefined) {
72+
(instance as Record<string | symbol, unknown>)[property] = injection;
73+
}
7274
});
7375
return instance
7476
}

Diff for: test/annotation/optional.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,49 @@ describe('@optional', () => {
107107

108108
});
109109

110+
it("Should allow to set a default value for class property dependencies flagged as optional", () => {
111+
@injectable()
112+
class Katana {
113+
public name: string;
114+
public constructor() {
115+
this.name = 'Katana';
116+
}
117+
}
118+
119+
@injectable()
120+
class Shuriken {
121+
public name: string;
122+
public constructor() {
123+
this.name = 'Shuriken';
124+
}
125+
}
126+
127+
@injectable()
128+
class Ninja {
129+
public name: string = "Ninja";
130+
@inject("Katana") public katana?: Katana;
131+
@inject("Shuriken") @optional() public shuriken: Shuriken = {
132+
name: "DefaultShuriken",
133+
};
134+
}
135+
136+
const container = new Container();
137+
138+
container.bind<Katana>('Katana').to(Katana);
139+
container.bind<Ninja>('Ninja').to(Ninja);
140+
141+
let ninja = container.get<Ninja>('Ninja');
142+
expect(ninja.name).to.eql('Ninja');
143+
expect(ninja.katana?.name).to.eql('Katana');
144+
expect(ninja.shuriken.name).to.eql('DefaultShuriken');
145+
146+
container.bind<Shuriken>('Shuriken').to(Shuriken);
147+
148+
ninja = container.get<Ninja>('Ninja');
149+
expect(ninja.name).to.eql('Ninja');
150+
expect(ninja.katana?.name).to.eql('Katana');
151+
expect(ninja.shuriken.name).to.eql('Shuriken');
152+
}
153+
);
154+
110155
});

Diff for: wiki/optional_dependencies.md

+10
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,13 @@ class Ninja {
8484
}
8585
}
8686
```
87+
88+
Or using properties injection:
89+
```ts
90+
@injectable()
91+
class Ninja {
92+
public name = "Ninja";
93+
@inject("Katana") public katana: Katana;
94+
@inject("Shuriken") @optional() public shuriken: Shuriken = { name: "DefaultShuriken" } // Default value!
95+
}
96+
```

0 commit comments

Comments
 (0)