Skip to content

Commit 80c5c68

Browse files
committed
chore: update CHANGELOG.md
1 parent cb9785d commit 80c5c68

File tree

5 files changed

+112
-1
lines changed

5 files changed

+112
-1
lines changed

.changeset/true-lemons-happen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@webiny/di": patch
3+
---
4+
5+
enforce typechecking of the dependencies array

__tests__/container.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,62 @@ describe("DIContainer", () => {
193193
expect(numberOfLogCalls).toBe(2);
194194
});
195195

196+
test("should resolve a composite wrapper using manual instances", () => {
197+
let numberOfLogCalls = 0;
198+
199+
// For assertion purposes, we create a logger decorator.
200+
class LoggerDecorator implements ILogger {
201+
constructor(private decoratee: ILogger) {}
202+
203+
log(): void {
204+
numberOfLogCalls++;
205+
}
206+
}
207+
208+
const loggerDecorator = createDecorator({
209+
abstraction: LoggerAbstraction,
210+
decorator: LoggerDecorator,
211+
dependencies: []
212+
});
213+
214+
const consoleLoggerImpl = createImplementation({
215+
abstraction: LoggerAbstraction,
216+
implementation: ConsoleLogger,
217+
dependencies: []
218+
});
219+
220+
const compositeImpl = createComposite({
221+
abstraction: LoggerAbstraction,
222+
implementation: CompositeLogger,
223+
dependencies: [[LoggerAbstraction, { multiple: true }]]
224+
});
225+
226+
rootContainer.register(consoleLoggerImpl);
227+
rootContainer.registerDecorator(loggerDecorator);
228+
rootContainer.registerComposite(compositeImpl);
229+
230+
rootContainer.registerInstance(LoggerAbstraction, new FileLogger());
231+
232+
class InjectionTest {
233+
constructor(public logger: ILogger) {}
234+
235+
getLogger() {
236+
return this.logger;
237+
}
238+
}
239+
240+
const testObject = rootContainer.resolveWithDependencies({
241+
implementation: InjectionTest,
242+
dependencies: [LoggerAbstraction]
243+
});
244+
245+
const compositeLogger = testObject.getLogger();
246+
expect(compositeLogger instanceof CompositeLogger).toBe(true);
247+
248+
compositeLogger.log("Composite log!");
249+
expect(numberOfLogCalls).toBe(2);
250+
});
251+
196252
test("should resolve multiple singleton implementations of the same abstraction when multiple flag is used", () => {
197253
const consoleLoggerImpl = createImplementation({
198254
abstraction: LoggerAbstraction,

__tests__/types.test-d.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { describe, test } from "vitest";
2+
import { Abstraction } from "~/index.js";
3+
4+
interface ILogger {
5+
log(...args: unknown[]): void;
6+
}
7+
8+
interface IFormatter {
9+
format(message: string): string;
10+
}
11+
12+
const LoggerAbstraction = new Abstraction<ILogger>("LoggerAbstraction");
13+
const FormatterAbstraction = new Abstraction<IFormatter>("FormatterAbstraction");
14+
15+
describe("Container Types", () => {
16+
test("dependencies types must be enforced", () => {
17+
interface IUseCase {}
18+
19+
const UseCaseAbstraction = new Abstraction<IUseCase>("UseCase");
20+
21+
class UseCase {
22+
constructor(
23+
private logger: ILogger,
24+
private formatter: IFormatter
25+
) {}
26+
}
27+
28+
// Correct assignment!!
29+
UseCaseAbstraction.createImplementation({
30+
implementation: UseCase,
31+
dependencies: [LoggerAbstraction, FormatterAbstraction]
32+
});
33+
34+
// Invalid assignment!!
35+
UseCaseAbstraction.createImplementation({
36+
implementation: UseCase,
37+
// @ts-expect-error is not assignable to type
38+
dependencies: [LoggerAbstraction]
39+
});
40+
41+
// Invalid assignment!!
42+
UseCaseAbstraction.createImplementation({
43+
implementation: UseCase,
44+
// @ts-expect-error is not assignable to type
45+
dependencies: [LoggerAbstraction, LoggerAbstraction]
46+
});
47+
});
48+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"build": "tsup src/index.ts --format esm --dts",
2323
"release": "pnpm run build && changeset publish",
2424
"lint": "tsc",
25-
"test": "vitest --run"
25+
"test": "vitest --run --typecheck"
2626
},
2727
"dependencies": {
2828
"reflect-metadata": "^0.2.2"

src/Abstraction.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type Implementation<A extends Abstraction<any>, I extends Constructor> = I & {
99

1010
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1111
export class Abstraction<T> {
12+
// If the generic type is not used in any way, TS simply ignores it, thus breaking the desired type checking.
13+
private readonly __type?: T;
1214
public readonly token: symbol;
1315

1416
constructor(name: string) {

0 commit comments

Comments
 (0)