Skip to content

Commit b7449d1

Browse files
committed
feat: add registerServiceWithFactory
1 parent 3ad871f commit b7449d1

File tree

4 files changed

+44
-5
lines changed

4 files changed

+44
-5
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ The concept of "scope" can vary depending on the framework, but here we call "sc
4242

4343
You are not required to setup a scope if you are not going to use scoped services or values.
4444

45-
## `registerService(lifetime, class)`
45+
## `registerService(lifetime, class, ...args)` and `registerServiceWithFactory(lifetime, class, factory)`
4646

4747
Registers a new service with the specified lifetime.
4848

@@ -90,6 +90,12 @@ class CoolIntegrationService {
9090
registerService("transient", CoolIntegrationService, axios.create(), env.COOL_API_KEY);
9191
```
9292

93+
If you need some custom construction logic and for some reason the class constructor can't be used for that, you can use `registerServiceWithFactory`. This function will be called only when needed.
94+
95+
```typescript
96+
registerServiceWithFactory("singleton", SomeService, () => SomeService.getInstance());
97+
```
98+
9399
## `use(class)`
94100

95101
This function can be called from anywhere (including from the constructor of a service) to obtain an instance of a service ready to use. The lifetime of the service is handled behind the scenes.

spec/inject.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
/* eslint-disable @typescript-eslint/no-shadow */
2-
import { popInjectionContext, pushInjectionContext, registerValue, registerScopedValue, registerService, setupScope, use } from "../src";
2+
import {
3+
popInjectionContext,
4+
pushInjectionContext,
5+
registerValue,
6+
registerScopedValue,
7+
registerService,
8+
setupScope,
9+
use,
10+
registerServiceWithFactory,
11+
} from "../src";
312

413
describe("env", () => {
514
beforeEach(pushInjectionContext);
@@ -411,4 +420,24 @@ describe("env", () => {
411420
popInjectionContext();
412421
}
413422
});
423+
424+
it("allow registering a service with a custom factory function", () => {
425+
class A {
426+
id = 0;
427+
}
428+
429+
const factory = jest.fn(() => {
430+
const instance = new A();
431+
432+
instance.id = 1;
433+
434+
return instance;
435+
});
436+
437+
registerServiceWithFactory("singleton", A, factory);
438+
439+
expect(use(A)).toBeInstanceOf(A);
440+
expect(use(A).id).toBe(1);
441+
expect(factory).toBeCalledTimes(1);
442+
});
414443
});

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { pushInjectionContext, popInjectionContext } from "./global-context";
22
export { setupScope } from "./scope-context";
3-
export { registerService } from "./service";
3+
export { registerService, registerServiceWithFactory } from "./service";
44
export { registerScopedValue, registerValue } from "./value";
55
export { use } from "./use";
66

src/service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ function createServiceInstance(service: Service) {
3636
}
3737
}
3838

39-
export function registerService<T extends ServiceType>(lifetime: ServiceLifetime, type: T, ...ctorArgs: ConstructorParameters<T>) {
39+
export function registerServiceWithFactory<T extends ServiceType>(lifetime: ServiceLifetime, type: T, factory: () => InstanceType<T>) {
4040
if (getGlobalContext().hasServiceSkipParent(type.name)) {
4141
throw new Error(`Service '${type.name}' is already registered`);
4242
}
4343

44-
getGlobalContext().setService(type.name, { type, factory: () => new type(...(ctorArgs as unknown[])), lifetime });
44+
getGlobalContext().setService(type.name, { type, factory, lifetime });
45+
}
46+
47+
export function registerService<T extends ServiceType>(lifetime: ServiceLifetime, type: T, ...ctorArgs: ConstructorParameters<T>) {
48+
registerServiceWithFactory(lifetime, type, () => new type(...(ctorArgs as unknown[])));
4549
}
4650

4751
export function useService<T extends ServiceType>(type: T): InstanceType<T> {

0 commit comments

Comments
 (0)