Skip to content

Commit 40d0fb1

Browse files
committed
Merge branch 'master' of https://github.com/nicojs/typed-inject into nicojs-master
2 parents 32ff668 + 5d3c027 commit 40d0fb1

29 files changed

+3827
-8633
lines changed

.eslintrc

Lines changed: 0 additions & 62 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
node-version: [16.x, 20.x]
16+
node-version: [18.x, 22.x]
1717
steps:
1818
- uses: actions/checkout@v2
1919
- uses: actions/setup-node@v2

.prettierrc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
{
2-
"singleQuote": true,
3-
"printWidth": 150
4-
}
1+
{ "singleQuote": true }

.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"args": [
3838
"--no-timeout",
3939
"--colors",
40+
"--bail",
4041
"${workspaceFolder}/dist/test/helpers/**/*.js",
4142
"${workspaceFolder}/dist/test/integration/**/*.js"
4243
],

.vscode/settings.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
{
22
"editor.codeActionsOnSave": {
3-
"source.fixAll.eslint": true
3+
"source.fixAll.eslint": "explicit"
44
},
5-
"eslint.validate": [
6-
"javascript",
7-
"typescript"
8-
],
5+
"eslint.validate": ["javascript", "typescript"],
96
"typescript.tsdk": "node_modules\\typescript\\lib",
10-
"cSpell.words": [
11-
"Typesafe"
12-
]
7+
"cSpell.words": ["tseslint", "Typesafe"],
8+
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }]
139
}

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
# [5.0.0](https://github.com/nicojs/typed-inject/compare/v4.0.0...v5.0.0) (2024-12-13)
2+
3+
4+
### Features
5+
6+
* **child injector:** Add `createChildInjector` ([#72](https://github.com/nicojs/typed-inject/issues/72)) ([e103564](https://github.com/nicojs/typed-inject/commit/e10356495f27428db85c6a074f369364cfad4871))
7+
* improve error messages ([#66](https://github.com/nicojs/typed-inject/issues/66)) ([64f7640](https://github.com/nicojs/typed-inject/commit/64f7640a68b76b3d4cac979110798333339309e4))
8+
* **node:** drop support for node 16 ([#71](https://github.com/nicojs/typed-inject/issues/71)) ([028cd45](https://github.com/nicojs/typed-inject/commit/028cd4553383521e9b7761bba327d545faabf4cc))
9+
10+
11+
### BREAKING CHANGES
12+
13+
* **node:** Please use Node 18 or higher
14+
15+
16+
117
# [4.0.0](https://github.com/nicojs/typed-inject/compare/v3.0.1...v4.0.0) (2023-05-05)
218

319

README.md

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@ _If you are new to 'Dependency Injection'/'Inversion of control', please read up
1414

1515
_If you want to know more about how typed-inject works, please read [my blog article about it](https://medium.com/@jansennico/advanced-typescript-type-safe-dependency-injection-873426e2cc96)_
1616

17-
18-
* [🗺️ Installation](#installation)
19-
* [🎁 Usage](#usage)
20-
* [💭 Motivation](#motivation)
21-
* [🗝️ Typesafe? How?](#typesafe-how)
22-
* [👶 Child injectors](#child-injectors)
23-
* [🎄 Decorate your dependencies](#decorate-your-dependencies)
24-
* [♻ Lifecycle control](#lifecycle-control)
25-
* [🚮 Disposing provided stuff](#disposing-provided-stuff)
26-
* [✨ Magic tokens](#magic-tokens)
27-
* [😬 Error handling](#error-handling)
28-
* [📖 API reference](#api-reference)
29-
* [🤝 Commendation](#commendation)
17+
- [🗺️ Installation](#installation)
18+
- [🎁 Usage](#usage)
19+
- [💭 Motivation](#motivation)
20+
- [🗝️ Typesafe? How?](#typesafe-how)
21+
- [👶 Child injectors](#child-injectors)
22+
- [🎄 Decorate your dependencies](#decorate-your-dependencies)
23+
- [♻ Lifecycle control](#lifecycle-control)
24+
- [🚮 Disposing provided stuff](#disposing-provided-stuff)
25+
- [✨ Magic tokens](#magic-tokens)
26+
- [😬 Error handling](#error-handling)
27+
- [📖 API reference](#api-reference)
28+
- [🤝 Commendation](#commendation)
3029

3130
<a name="installation"></a>
3231

@@ -66,7 +65,7 @@ interface Logger {
6665
const logger: Logger = {
6766
info(message: string) {
6867
console.log(message);
69-
}
68+
},
7069
};
7170

7271
class HttpClient {
@@ -75,11 +74,16 @@ class HttpClient {
7574
}
7675

7776
class MyService {
78-
constructor(private http: HttpClient, private log: Logger) {}
77+
constructor(
78+
private http: HttpClient,
79+
private log: Logger,
80+
) {}
7981
public static inject = ['httpClient', 'logger'] as const;
8082
}
8183

82-
const appInjector = createInjector().provideValue('logger', logger).provideClass('httpClient', HttpClient);
84+
const appInjector = createInjector()
85+
.provideValue('logger', logger)
86+
.provideClass('httpClient', HttpClient);
8387

8488
const myService = appInjector.injectClass(MyService);
8589
// Dependencies for MyService validated and injected
@@ -105,12 +109,17 @@ class HttpClient {
105109
}
106110

107111
class MyService {
108-
constructor(private http: HttpClient, private log: Logger) {}
112+
constructor(
113+
private http: HttpClient,
114+
private log: Logger,
115+
) {}
109116
public static inject = ['logger', 'httpClient'] as const;
110117
// ERROR! Types of parameters 'http' and 'args_0' are incompatible
111118
}
112119

113-
const appInjector = createInjector().provideValue('logger', logger).provideClass('httpClient', HttpClient);
120+
const appInjector = createInjector()
121+
.provideValue('logger', logger)
122+
.provideClass('httpClient', HttpClient);
114123

115124
const myService = appInjector.injectClass(MyService);
116125
```
@@ -205,12 +214,14 @@ function fooDecorator(foo: Foo) {
205214
console.log('before call');
206215
foo.bar();
207216
console.log('after call');
208-
}
217+
},
209218
};
210219
}
211220
fooDecorator.inject = ['foo'] as const;
212221

213-
const fooProvider = createInjector().provideClass('foo', Foo).provideFactory('foo', fooDecorator);
222+
const fooProvider = createInjector()
223+
.provideClass('foo', Foo)
224+
.provideFactory('foo', fooDecorator);
214225
const foo = fooProvider.resolve('foo');
215226

216227
foo.bar();
@@ -240,7 +251,9 @@ class Foo {
240251
static inject = ['log'] as const;
241252
}
242253

243-
const fooProvider = injector.provideFactory('log', loggerFactory, Scope.Transient).provideClass('foo', Foo, Scope.Singleton);
254+
const fooProvider = injector
255+
.provideFactory('log', loggerFactory, Scope.Transient)
256+
.provideClass('foo', Foo, Scope.Singleton);
244257
const foo = fooProvider.resolve('foo');
245258
const fooCopy = fooProvider.resolve('foo');
246259
const log = fooProvider.resolve('log');
@@ -349,13 +362,13 @@ class Bar {
349362
}
350363
class Baz {
351364
static inject = ['foo', 'bar'] as const;
352-
constructor(public foo: Foo, public bar: Bar) {}
365+
constructor(
366+
public foo: Foo,
367+
public bar: Bar,
368+
) {}
353369
}
354370
const rootInjector = createInjector();
355-
rootInjector
356-
.provideClass('foo', Foo)
357-
.provideClass('bar', Bar)
358-
.injectClass(Baz);
371+
rootInjector.provideClass('foo', Foo).provideClass('bar', Bar).injectClass(Baz);
359372
await fooProvider.dispose();
360373
// => "Foo disposed"
361374
// => "Bar disposed",
@@ -369,15 +382,20 @@ Any instance created with `injectClass` or `injectFactory` will _not_ be dispose
369382
370383
Any `Injector` instance can always provide the following tokens:
371384
372-
| Token name | Token value | Description |
373-
| ---------------- | ------------- | -------------------------------------------------------------------------------------------------- |
374-
| `INJECTOR_TOKEN` | `'$injector'` | Injects the current injector |
385+
| Token name | Token value | Description |
386+
| ---------------- | ------------- | --------------------------------------------------------------------------------------------------- |
387+
| `INJECTOR_TOKEN` | `'$injector'` | Injects the current injector |
375388
| `TARGET_TOKEN` | `'$target'` | The class or function in which the current values are injected, or `undefined` if resolved directly |
376389
377390
An example:
378391
379392
```ts
380-
import { createInjector, Injector, TARGET_TOKEN, INJECTOR_TOKEN } from 'typed-inject';
393+
import {
394+
createInjector,
395+
Injector,
396+
TARGET_TOKEN,
397+
INJECTOR_TOKEN,
398+
} from 'typed-inject';
381399

382400
class Foo {
383401
constructor(injector: Injector<{}>, target: Function | undefined) {}
@@ -500,7 +518,7 @@ const foo /*: Foo*/ = injector.injectClass(Foo);
500518
501519
#### `injector.injectFunction(fn: InjectableFunction)`
502520
503-
This method injects the function with requested tokens from the injector, invokes it and returns the result.
521+
This method injects the function with requested tokens from the injector, invokes it and returns the result.
504522
505523
It is a shortcut for calling the provided function with the values from the injector.
506524
@@ -554,22 +572,44 @@ function loggerFactory(target: Function | undefined) {
554572
return new Logger((target && target.name) || '');
555573
}
556574
loggerFactory.inject = [TARGET_TOKEN] as const;
557-
const fooBarInjector = fooInjector.provideFactory('logger', loggerFactory, Scope.Transient);
575+
const fooBarInjector = fooInjector.provideFactory(
576+
'logger',
577+
loggerFactory,
578+
Scope.Transient,
579+
);
558580
```
559581
560-
#### `injector.provideFactory(token: Token, Class: InjectableClass<TContext>, scope = Scope.Singleton): Injector<ChildContext<TContext, Token, R>>`
582+
#### `injector.provideClass(token: Token, Class: InjectableClass<TContext>, scope = Scope.Singleton): Injector<ChildContext<TContext, Token, R>>`
561583
562584
Create a child injector that can provide a value using instances of `Class` for token `'token'`. The new child injector can resolve all tokens the parent injector can, as well as the new `'token'`.
563585
564586
Scope is also supported here, for more info, see `provideFactory`.
565587
588+
#### `injector.createChildInjector(): Injector<TContext>`
589+
590+
Create a child injector that can provide exactly the same as the parent injector. Contrary to its `provideXxx` counterparts,this will create a new disposable scope without providing additional injectable values.
591+
592+
```ts
593+
const parentInjector = createInjector().provideValue('foo', 'bar');
594+
for (const task of tasks) {
595+
try {
596+
const scope = parentInjector.createChildInjector();
597+
const foo = scope.provideClass('baz', DisposableBaz).injectClass(Foo);
598+
foo.handle(task);
599+
} finally {
600+
await scope.dispose(); // Dispose the scope, including instances of DisposableBaz
601+
// Next task gets a fresh scope
602+
}
603+
}
604+
```
605+
566606
#### `injector.dispose(): Promise<void>`
567607
568608
Use `dispose` to explicitly dispose the `injector`. This will result in the following (in order):
569609
570610
1. Call `dispose` on each child injector created from this injector.
571-
2. It will call `dispose` on any dependency created by the injector (if it exists) using `provideClass` or `provideFactory` (**not** `provideValue` or `injectXXX`).
572-
3. It will also await any promise that might have been returned by disposable dependencies.
611+
2. It will call `dispose` on any dependency created by the injector (if it exists) using `provideClass` or `provideFactory` (**not** `provideValue` or `injectXXX`).
612+
3. It will also await any promise that might have been returned by disposable dependencies.
573613
574614
_Note: this behavior changed since v2. Before v2, the parent injector was always disposed before the child injector._
575615
_Note: this behavior changed again in v3, calling `dispose` on a child injector will **no longer** dispose it's parent injector and instead will dispose it's child injectors. The order of disposal is still child first._

0 commit comments

Comments
 (0)