Socket
Socket
Sign inDemoInstall

typed-inject

Package Overview
Dependencies
0
Maintainers
2
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.2.1 to 3.0.0

41

CHANGELOG.md

@@ -0,1 +1,42 @@

# [3.0.0](https://github.com/nicojs/typed-inject/compare/v2.2.0...v3.0.0) (2020-08-12)
### Bug Fixes
* **dist:** distribute ts alongside dist js code ([#21](https://github.com/nicojs/typed-inject/issues/21)) ([0b178b5](https://github.com/nicojs/typed-inject/commit/0b178b5f8fa919a421e96d22f30e28ab179a2417))
### Features
* **as const:** allow to declare tokens as const ([#27](https://github.com/nicojs/typed-inject/issues/27)) ([05435b7](https://github.com/nicojs/typed-inject/commit/05435b7c5018d30a0df7a8f06948284cd678262b))
* **dispose:** dispose child injectors ([#29](https://github.com/nicojs/typed-inject/issues/29)) ([3f0f3f5](https://github.com/nicojs/typed-inject/commit/3f0f3f58f990a2f68e9545c5c0f0ba7b2c3a50b7))
* **maintanance:** drop node 8 support ([#28](https://github.com/nicojs/typed-inject/issues/28)) ([cdf3c30](https://github.com/nicojs/typed-inject/commit/cdf3c3046fd320d63be3e950d15d31b83d2679ec))
### BREAKING CHANGES
* **dispose:** `rootInjector` is removed in favor of `createInjector`.
This:
```
import { rootInjector } from 'typed-inject';
```
Becomes:
```
import { createInjector } from 'typed-inject';
const rootInjector = createInjector();
```
Injector's created from `createInjector` are no longer stateless. They
keep track of their child injectors.
* **dispose:** `dispose` no longer disposes parent injector, disposes
the child injectors instead. See readme for more details.
* **maintanance:** Node 8 is no longer supported.
* **as const:** The typed-inject is now expecting tokens to be provided in a `readonly` array. You can either use `as const` or the `tokens` helper function for it.
## [2.2.1](https://github.com/nicojs/typed-inject/compare/v2.2.0...v2.2.1) (2020-03-22)

@@ -2,0 +43,0 @@

2

dist/src/api/CorrespondingType.d.ts
import { InjectionToken, InjectorToken, TargetToken } from './InjectionToken';
import { Injector } from './Injector';
export declare type CorrespondingType<TContext, T extends InjectionToken<TContext>> = T extends InjectorToken ? Injector<TContext> : T extends TargetToken ? Function | undefined : T extends keyof TContext ? TContext[T] : never;
export declare type CorrespondingTypes<TContext, TS extends InjectionToken<TContext>[]> = {
export declare type CorrespondingTypes<TContext, TS extends readonly InjectionToken<TContext>[]> = {
[K in keyof TS]: TS[K] extends InjectionToken<TContext> ? CorrespondingType<TContext, TS[K]> : never;
};
//# sourceMappingURL=CorrespondingType.d.ts.map
import { CorrespondingTypes } from './CorrespondingType';
import { InjectionToken } from './InjectionToken';
export declare type InjectableClass<TContext, R, Tokens extends InjectionToken<TContext>[]> = ClassWithInjections<TContext, R, Tokens> | ClassWithoutInjections<R>;
export interface ClassWithInjections<TContext, R, Tokens extends InjectionToken<TContext>[]> {
export declare type InjectableClass<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> = ClassWithInjections<TContext, R, Tokens> | ClassWithoutInjections<R>;
export interface ClassWithInjections<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> {
new (...args: CorrespondingTypes<TContext, Tokens>): R;

@@ -9,4 +9,4 @@ readonly inject: Tokens;

export declare type ClassWithoutInjections<R> = new () => R;
export declare type InjectableFunction<TContext, R, Tokens extends InjectionToken<TContext>[]> = InjectableFunctionWithInject<TContext, R, Tokens> | InjectableFunctionWithoutInject<R>;
export interface InjectableFunctionWithInject<TContext, R, Tokens extends InjectionToken<TContext>[]> {
export declare type InjectableFunction<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> = InjectableFunctionWithInject<TContext, R, Tokens> | InjectableFunctionWithoutInject<R>;
export interface InjectableFunctionWithInject<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> {
(...args: CorrespondingTypes<TContext, Tokens>): R;

@@ -16,3 +16,3 @@ readonly inject: Tokens;

export declare type InjectableFunctionWithoutInject<R> = () => R;
export declare type Injectable<TContext, R, Tokens extends InjectionToken<TContext>[]> = InjectableClass<TContext, R, Tokens> | InjectableFunction<TContext, R, Tokens>;
export declare type Injectable<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> = InjectableClass<TContext, R, Tokens> | InjectableFunction<TContext, R, Tokens>;
//# sourceMappingURL=Injectable.d.ts.map

@@ -6,10 +6,10 @@ import { InjectableClass, InjectableFunction } from './Injectable';

export interface Injector<TContext = {}> {
injectClass<R, Tokens extends InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>): R;
injectFunction<R, Tokens extends InjectionToken<TContext>[]>(Class: InjectableFunction<TContext, R, Tokens>): R;
injectClass<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>): R;
injectFunction<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableFunction<TContext, R, Tokens>): R;
resolve<Token extends keyof TContext>(token: Token): TContext[Token];
provideValue<Token extends string, R>(token: Token, value: R): Injector<TChildContext<TContext, R, Token>>;
provideClass<Token extends string, R, Tokens extends InjectionToken<TContext>[]>(token: Token, Class: InjectableClass<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
provideFactory<Token extends string, R, Tokens extends InjectionToken<TContext>[]>(token: Token, factory: InjectableFunction<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
provideClass<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token, Class: InjectableClass<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
provideFactory<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(token: Token, factory: InjectableFunction<TContext, R, Tokens>, scope?: Scope): Injector<TChildContext<TContext, R, Token>>;
dispose(): Promise<void>;
}
//# sourceMappingURL=Injector.d.ts.map
import { Injector } from './api/Injector';
export declare const rootInjector: Injector<{}>;
export declare function createInjector(): Injector<{}>;
//# sourceMappingURL=InjectorImpl.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
const Scope_1 = require("./api/Scope");

@@ -30,2 +33,6 @@ const InjectionToken_1 = require("./api/InjectionToken");

class AbstractInjector {
constructor() {
this.childInjectors = [];
this.isDisposed = false;
}
injectClass(Class, providedIn) {

@@ -53,3 +60,3 @@ this.throwIfDisposed(Class);

const tokens = injectable.inject || [];
return tokens.map(key => {
return tokens.map((key) => {
switch (key) {

@@ -66,9 +73,18 @@ case InjectionToken_1.TARGET_TOKEN:

provideValue(token, value) {
return new ValueProvider(this, token, value);
this.throwIfDisposed(token);
const provider = new ValueProvider(this, token, value);
this.childInjectors.push(provider);
return provider;
}
provideClass(token, Class, scope = DEFAULT_SCOPE) {
return new ClassProvider(this, token, scope, Class);
this.throwIfDisposed(token);
const provider = new ClassProvider(this, token, scope, Class);
this.childInjectors.push(provider);
return provider;
}
provideFactory(token, factory, scope = DEFAULT_SCOPE) {
return new FactoryProvider(this, token, scope, factory);
this.throwIfDisposed(token);
const provider = new FactoryProvider(this, token, scope, factory);
this.childInjectors.push(provider);
return provider;
}

@@ -84,2 +100,11 @@ resolve(token, target) {

}
async dispose() {
if (!this.isDisposed) {
this.isDisposed = true; // be sure new disposables aren't added while we're disposing
await Promise.all(this.childInjectors.map((child) => child.dispose()));
while (this.childInjectors.pop())
; // Don't keep the references, might cause a memory leak
await this.disposeInjectedValues();
}
}
}

@@ -90,8 +115,5 @@ class RootInjector extends AbstractInjector {

}
dispose() {
disposeInjectedValues() {
return Promise.resolve();
}
get isDisposed() {
return false;
}
}

@@ -105,14 +127,5 @@ class ChildInjector extends AbstractInjector {

this.disposables = new Set();
this.isDisposed = false;
}
async dispose() {
if (!this.isDisposed) {
this.isDisposed = true; // be sure new disposables aren't added while we're disposing
await this.disposeInjectedValues();
this.disposables.clear();
await this.parent.dispose();
}
}
async disposeInjectedValues() {
const promisesToAwait = [...this.disposables.values()].map(disposable => disposable.dispose());
const promisesToAwait = [...this.disposables.values()].map((disposable) => disposable.dispose());
await Promise.all(promisesToAwait);

@@ -179,3 +192,6 @@ }

}
exports.rootInjector = new RootInjector();
function createInjector() {
return new RootInjector();
}
exports.createInjector = createInjector;
//# sourceMappingURL=InjectorImpl.js.map

@@ -10,3 +10,3 @@ /**

*/
export declare function tokens<TS extends string[]>(...tokens: TS): TS;
export declare function tokens<TS extends readonly string[]>(...tokens: TS): TS;
//# sourceMappingURL=tokens.d.ts.map
import { Disposable } from './api/Disposable';
export declare function isDisposable(maybeDisposable: any): maybeDisposable is Disposable;
export declare function isDisposable(maybeDisposable: unknown): maybeDisposable is Disposable;
//# sourceMappingURL=utils.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function isDisposable(maybeDisposable) {
return maybeDisposable && maybeDisposable.dispose && typeof maybeDisposable.dispose === 'function';
const asDisposable = maybeDisposable;
return asDisposable && asDisposable.dispose && typeof asDisposable.dispose === 'function';
}
exports.isDisposable = isDisposable;
//# sourceMappingURL=utils.js.map
{
"name": "typed-inject",
"version": "2.2.1",
"version": "3.0.0",
"description": "Type safe dependency injection framework for TypeScript",

@@ -10,3 +10,3 @@ "main": "dist/src/index.js",

"clean": "rimraf dist",
"lint": "eslint . --ext .js,.ts --ignore-path .gitignore --ignore-pattern testResources/**/*.ts --ignore-pattern stryker.conf.js",
"lint": "eslint . --ext .js,.ts --ignore-path .gitignore --ignore-pattern testResources/**/*.ts --ignore-pattern stryker.conf.js --ignore-pattern .eslintrc.js",
"build": "tsc -b",

@@ -29,3 +29,3 @@ "test": "nyc --exclude-after-remap=false \"--exclude=dist/test/**/*.js\" --check-coverage --reporter=html --report-dir=reports/coverage --lines 100 --functions 100 --branches 100 npm run mocha",

"engines": {
"node": ">=8"
"node": ">=10"
},

@@ -50,28 +50,26 @@ "keywords": [

"devDependencies": {
"@stryker-mutator/core": "^2.5.0",
"@stryker-mutator/html-reporter": "^2.5.0",
"@stryker-mutator/mocha-framework": "^2.5.0",
"@stryker-mutator/mocha-runner": "^2.5.0",
"@stryker-mutator/typescript": "^2.5.0",
"@types/chai": "^4.2.7",
"@types/mocha": "^5.2.7",
"@types/node": "^13.1.7",
"@types/sinon": "^7.5.1",
"@types/sinon-chai": "^3.2.3",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
"@stryker-mutator/core": "^4.0.0-beta.2",
"@stryker-mutator/mocha-runner": "^4.0.0-beta.2",
"@stryker-mutator/typescript-checker": "^4.0.0-beta.2",
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.1",
"@types/node": "^14.0.27",
"@types/sinon": "^9.0.4",
"@types/sinon-chai": "^3.2.4",
"@typescript-eslint/eslint-plugin": "^3.9.0",
"@typescript-eslint/parser": "^3.9.0",
"chai": "^4.2.0",
"conventional-changelog-cli": "^2.0.31",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-prettier": "^3.1.2",
"mocha": "^7.0.0",
"nyc": "^15.0.0",
"prettier": "^1.19.1",
"rimraf": "^3.0.0",
"sinon": "^8.0.4",
"sinon-chai": "^3.4.0",
"source-map-support": "^0.5.16",
"conventional-changelog-cli": "^2.0.34",
"eslint": "^7.6.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"mocha": "^8.1.1",
"nyc": "^15.1.0",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"sinon": "^9.0.3",
"sinon-chai": "^3.5.0",
"source-map-support": "^0.5.19",
"typescript": "^3.7.4"
}
}

@@ -9,5 +9,5 @@ [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fnicojs%2Ftyped-inject%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/nicojs/typed-inject/master)

> Type safe dependency injection for TypeScript
> Typesafe dependency injection for TypeScript
A tiny, 100% type safe dependency injection framework for TypeScript. You can inject classes, interfaces or primitives. If your project compiles, you know for sure your dependencies are resolved at runtime and have their declared types.
A tiny, 100% typesafe dependency injection framework for TypeScript. You can inject classes, interfaces, or primitives. If your project compiles, you know your dependencies are resolved at runtime and have their declared types.

@@ -22,7 +22,7 @@ _If you are new to 'Dependency Injection'/'Inversion of control', please read up on it [in this blog article about it](https://medium.com/@samueleresca/inversion-of-control-and-dependency-injection-in-typescript-3040d568aabe)_

* [💭 Motivation](#motivation)
* [🗝️ Type safe? How?](#type-safe-how)
* [🗝️ Typesafe? How?](#typesafe-how)
* [👶 Child injectors](#child-injectors)
* [🎄 Decorate your dependencies](#decorate-your-dependencies)
* [♻ Lifecycle control](#lifecycle-control)
* [🚮 Disposing provided stuff](#disposing-provided-stuff)
* [🚮 Disposing of provided stuff](#disposing-of-provided-stuff)
* [✨ Magic tokens](#magic-tokens)

@@ -53,3 +53,3 @@ * [😬 Error handling](#error-handling)

<a name="Usage"></a>
<a name="usage"></a>

@@ -61,3 +61,3 @@ ## 🎁 Usage

```ts
import { rootInjector, tokens } from 'typed-inject';
import { createInjector } from 'typed-inject';

@@ -76,3 +76,3 @@ interface Logger {

constructor(private log: Logger) {}
public static inject = tokens('logger');
public static inject = ['logger'] as const;
}

@@ -82,6 +82,6 @@

constructor(private http: HttpClient, private log: Logger) {}
public static inject = tokens('httpClient', 'logger');
public static inject = ['httpClient', 'logger'] as const;
}
const appInjector = rootInjector.provideValue('logger', logger).provideClass('httpClient', HttpClient);
const appInjector = createInjector().provideValue('logger', logger).provideClass('httpClient', HttpClient);

@@ -97,3 +97,3 @@ const myService = appInjector.injectClass(MyService);

Dependencies are resolved using the static `inject` property on their classes. They must match the names given to the dependencies when configuring the injector with `provideXXX` methods.
Dependencies are resolved using the static `inject` property in their classes. They must match the names given to the dependencies when configuring the injector with `provideXXX` methods.

@@ -103,3 +103,3 @@ Expect compiler errors when you mess up the order of tokens or forget it completely.

```ts
import { rootInjector, tokens } from 'typed-inject';
import { createInjector } from 'typed-inject';

@@ -115,7 +115,7 @@ // Same logger as before

constructor(private http: HttpClient, private log: Logger) {}
public static inject = tokens('logger', 'httpClient');
public static inject = ['logger', 'httpClient'] as const;
// ERROR! Types of parameters 'http' and 'args_0' are incompatible
}
const appInjector = rootInjector.provideValue('logger', logger).provideClass('httpClient', HttpClient);
const appInjector = createInjector().provideValue('logger', logger).provideClass('httpClient', HttpClient);

@@ -135,13 +135,13 @@ const myService = appInjector.injectClass(MyService);

InversifyJS works with a nice API using decorators. Decorators is in Stage 2 of ecma script proposal at the moment of writing this, so will most likely land in ESNext. However, it also is opinionated in that it requires you to use [reflect-metadata](https://rbuckton.github.io/reflect-metadata/), which [is supposed to be an ecma script proposal, but isn't yet (at the moment of writing this)](https://github.com/rbuckton/reflect-metadata/issues/96). It might take years for reflect-metadata to land in Ecma script, if it ever does.
InversifyJS works with a nice API using decorators. Decorators are in Stage 2 of ecma script proposal at the moment of writing this, so they will most likely land in ESNext. However, it also is opinionated in that it requires you to use [reflect-metadata](https://rbuckton.github.io/reflect-metadata/), which [is supposed to be an ecma script proposal, but isn't yet (at the moment of writing this)](https://github.com/rbuckton/reflect-metadata/issues/96). It might take years for reflect-metadata to land in JavaScript, if it ever does.
### InversifyJS is not type-safe
### InversifyJS is not typesafe
InversifyJS is also _not_ type-safe. There is no check to see of the injected type is actually injectable or that the corresponding type adheres to the expected type.
InversifyJS is also _not_ typesafe. There is no check to see of the injected type is actually injectable or that the corresponding type adheres to the expected type.
<a name="type-safe-how"></a>
<a name="typesafe-how"></a>
## 🗝️ Type safe? How?
## 🗝️ Typesafe? How?
Type safe dependency injection works by combining awesome TypeScript features. Some of those features are:
Type safe dependency injection works by combining excellent TypeScript features. Some of those features are:

@@ -160,12 +160,12 @@ - [Literal types](https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types)

The `Injector` interface is responsible for injecting classes or functions. However, `typed-inject` only comes with one implementation: the `rootInjector`. It can't provide any dependencies directly (expect for [magic tokens](#-magic-tokens)).
The `Injector` interface is responsible for injecting classes or functions. You start off with an empty injector after calling `createInjector`. It can't provide any dependencies directly (expect for [magic tokens](#-magic-tokens)).
In order to do anything useful with the `rootInjector`, you'll need to create child injectors. This what you do with the `provideXXX` methods.
To do anything useful with your injector, you'll need to create child injectors. This what you do with the `provideXXX` methods.
```ts
import { rootInjector, tokens } from 'typed-inject';
import { createInjector } from 'typed-inject';
function barFactory(foo: number) {
return foo + 1;
}
barFactory.inject = tokens('foo');
barFactory.inject = ['foo'] as const;
class Baz {

@@ -175,9 +175,17 @@ constructor(bar: number) {

}
static inject = tokens('bar');
static inject = ['bar'] as const;
}
const childInjector = rootInjector
.provideValue('foo', 42)
.provideFactory('bar', barFactory)
.provideClass('baz', Baz);
// Create 3 child injectors here
const childInjector = createInjector()
.provideValue('foo', 42) // child injector can provide 'foo'
.provideFactory('bar', barFactory) // child injector can provide both 'bar' and 'foo'
.provideClass('baz', Baz); // child injector can provide 'baz', 'bar' and 'foo'
// Now use it here
function run(baz: Baz) {
// baz is created!
}
run.inject = ['baz'] as const;
childInjector.injectFunction(run);
```

@@ -187,4 +195,3 @@

The `rootInjector` always remains stateless. So don't worry about reusing it in your tests or reusing it for different parts of your application. However,
any ChildInjector _is stateful_. For example, it can [cache the injected value](#-control-lifecycle) or [keep track of stuff to dispose](#-disposing-provided-stuff)
Injectors keep track of their child injectors and values they've injected. This way it can provide functionality like [cache the injected value](#-control-lifecycle) or [keep track of stuff to dispose](#-disposing-provided-stuff).

@@ -198,3 +205,3 @@ <a name="decorate-your-dependencies"></a>

```ts
import { tokens, rootInjector } from 'typed-inject';
import { createInjector } from 'typed-inject';

@@ -216,5 +223,5 @@ class Foo {

}
fooDecorator.inject = tokens('foo');
fooDecorator.inject = ['foo'] as const;
const fooProvider = rootInjector.provideClass('foo', Foo).provideFactory('foo', fooDecorator);
const fooProvider = createInjector().provideClass('foo', Foo).provideFactory('foo', fooDecorator);
const foo = fooProvider.resolve('foo');

@@ -245,3 +252,3 @@

}
static inject = tokens('log');
static inject = ['log'] as const;
}

@@ -263,3 +270,3 @@

- `Scope.Transient`
Use `Scope.Transient` to completely disable cashing. You'll always get fresh instances.
Use `Scope.Transient` to altogether disable cashing. You'll always get fresh instances.

@@ -270,10 +277,10 @@ <a name="disposing-provided-stuff"></a>

Memory in JavaScript is garbage collected, so usually we don't care about cleaning up after ourselves. However, there might be a need to explicit clean up. For example removing a temp folder, or killing a child process.
Memory in JavaScript is garbage collected, so, we usually don't care about cleaning up after ourselves. However, there might be a need to explicit cleanup. For example removing a temp folder, or killing a child process.
As `typed-inject` is responsible for creating (providing) your dependencies, it only makes sense it is also responsible for the disposing of them.
Any `Injector` has a `dispose` method. If you call it, the injector in turn will call `dispose` on any instance that he provided (if it has one).
Any `Injector` has a `dispose` method. Calling it will call `dispose` on any instance that was ever provided from it, as well as any child injectors that were created from it.
```ts
import { rootInjector } from 'typed-inject';
import { createInjector } from 'typed-inject';

@@ -288,8 +295,11 @@ class Foo {

}
const rootInjector = createInjector();
const fooProvider = rootInjector.provideClass('foo', Foo);
fooProvider.resolve('foo'); // => "Foo created"
await fooProvider.dispose(); // => "Foo disposed"
await rootInjector.dispose(); // => "Foo disposed"
fooProvider.resolve('foo'); // Error: Injector already disposed
```
_Note: Always dispose from the top down! In this example, the `rootInjector` is disposed, which in turn disposes everything that was ever provided from one if it's child injectors._
To help you implementing the `dispose` method correctly, `typed-inject` exports the `Disposable` interface for convenience:

@@ -310,3 +320,3 @@

```ts
import { rootInjector, Disposable } from 'typed-inject';
import { createInjector, Disposable } from 'typed-inject';
class Foo implements Disposable {

@@ -317,2 +327,3 @@ dispose(): Promise<void> {

}
const rootInjector = createInjector();
const fooProvider = rootInjector

@@ -329,11 +340,12 @@ .provideClass('foo', Foo);

Using `dispose` on an injector will automatically dispose it's parent injectors as well:
Using `dispose` on the rootInjector will automatically dispose it's child injectors as well:
```ts
import { rootInjector } from 'typed-inject';
import { createInjector } from 'typed-inject';
class Foo {}
class Bar {}
const rootInjector = createInjector();
const fooProvider = rootInjector.provideClass('foo', Foo);
const barProvider = fooProvider.provideClass('bar', Bar);
await barProvider.dispose(); // => fooProvider is also disposed!
await rootInjector.dispose(); // => fooProvider is also disposed!
fooProvider.resolve('foo'); // => Error: Injector already disposed

@@ -345,3 +357,3 @@ ```

```ts
import { rootInjector, tokens } from 'typed-inject';
import { createInjector } from 'typed-inject';

@@ -359,5 +371,6 @@ class Foo {

class Baz {
static inject = tokens('foo', 'bar');
static inject = ['foo', 'bar'] as const;
constructor(public foo: Foo, public bar: Bar) {}
}
const rootInjector = createInjector();
rootInjector

@@ -383,3 +396,3 @@ .provideClass('foo', Foo)

| `INJECTOR_TOKEN` | `'$injector'` | Injects the current injector |
| `TARGET_TOKEN` | `'$target'` | The class or function in which the current values is injected, or `undefined` if resolved directly |
| `TARGET_TOKEN` | `'$target'` | The class or function in which the current values are injected, or `undefined` if resolved directly |

@@ -389,10 +402,10 @@ An example:

```ts
import { rootInjector, Injector, tokens, TARGET_TOKEN, INJECTOR_TOKEN } from 'typed-inject';
import { createInjector, Injector, TARGET_TOKEN, INJECTOR_TOKEN } from 'typed-inject';
class Foo {
constructor(injector: Injector<{}>, target: Function | undefined) {}
static inject = tokens(INJECTOR_TOKEN, TARGET_TOKEN);
static inject = [INJECTOR_TOKEN, TARGET_TOKEN] as const;
}
const foo = rootInjector.inject(Foo);
const foo = createInjector().inject(Foo);
```

@@ -416,9 +429,9 @@

constructor(public grandchild: GrandChild) {}
public static inject = tokens('grandChild');
public static inject = ['grandChild'] as const;
}
class Parent {
constructor(public readonly child: Child) {}
public static inject = tokens('child');
public static inject = ['child'] as const;
}
rootInjector
createInjector()
.provideClass('grandChild', GrandChild)

@@ -435,3 +448,3 @@ .provideClass('child', Child)

try {
rootInjector
createInjector()
.provideClass('grandChild', GrandChild)

@@ -453,2 +466,6 @@ .provideClass('child', Child)

### `createInjector`
Create a new `Injector<{}>`. You generally want to create one per application/request. If you're using `typed-inject` also in your unit tests, you probably want to create a fresh one for each test, for example in global test setup.
### `Injector<TContext>`

@@ -458,8 +475,6 @@

The `TContext` generic arguments is a [lookup type](https://blog.mariusschulz.com/2017/01/06/typescript-2-1-keyof-and-lookup-types). The keys in this type are the tokens that can be injected, the values are the exact types of those tokens. For example, if `TContext extends { foo: string, bar: number }`, you can let a token `'foo'` be injected of type `string`, and a token `'bar'` of type `number`.
The `TContext` generic argument is a [lookup type](https://blog.mariusschulz.com/2017/01/06/typescript-2-1-keyof-and-lookup-types). The keys in this type are the tokens that can be injected, the values are the exact types of those tokens. For example, if `TContext extends { foo: string, bar: number }`, you can let a token `'foo'` be injected of type `string`, and a token `'bar'` of type `number`.
Typed inject comes with only one implementation. The `rootInjector`. It implements `Injector<{}>` interface, meaning that it does not provide any tokens (except for [magic tokens](#-magic-tokens)). Import it with `import { rootInjector } from 'typed-inject'`. From the `rootInjector`, you can create child injectors. See [creating child injectors](#-creating-child-injectors) for more information.
Don't worry about reusing the `rootInjector` in your application. It is stateless and read-only, so safe for concurrent use.
#### `injector.injectClass(injectable: InjectableClass)`

@@ -473,3 +488,3 @@

constructor(bar: number) {}
static inject = tokens('bar');
static inject = ['bar'] as const;
}

@@ -488,3 +503,3 @@ const foo /*: Foo*/ = injector.injectClass(Foo);

}
foo.inject = tokens('bar');
foo.inject = ['bar'] as const;
const baz /*: number*/ = injector.injectFunction(Foo);

@@ -503,3 +518,3 @@ ```

}
retrieveFoo.inject = tokens('foo');
retrieveFoo.inject = ['foo'] as const;
const foo2 = injector.injectFunction(retrieveFoo);

@@ -518,3 +533,3 @@ ```

Create a child injector that can provide a value using `factory` for token `'token'`. The new child injector can resolve all tokens the parent injector can, as well as the new `'token'`.
Create a child injector that can provide a value using `factory` for token `'token'`. The new child injector can resolve all tokens the parent injector can and the new `'token'`.

@@ -528,3 +543,3 @@ With `scope` you can decide whether the value must be cached after the factory is invoked once. Use `Scope.Singleton` to enable caching (default), or `Scope.Transient` to disable caching.

}
loggerFactory.inject = tokens(TARGET_TOKEN);
loggerFactory.inject = [TARGET_TOKEN] as const;
const fooBarInjector = fooInjector.provideFactory('logger', loggerFactory, Scope.Transient);

@@ -541,10 +556,13 @@ ```

Use `dispose` to explicitly dispose the `injector`. It will call `dispose` on any dependency created by the injector (if it exists) using `provideClass` or `provideFactory` (**not** `provideValue` or `injectXXX`). It will also await any promise that might have been returned by `dispose`. After that, it will `dispose` it's parent injector as well.
Use `dispose` to explicitly dispose the `injector`. This will result in the following (in order):
1. Call `dispose` on each child injector created from this injector.
2. It will call `dispose` on any dependency created by the injector (if it exists) using `provideClass` or `provideFactory` (**not** `provideValue` or `injectXXX`).
3. It will also await any promise that might have been returned by disposable dependencies.
_Note: this behavior changed since v2. Before v2, the parent injector was always disposed before the child injector._
_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._
After a child injector is disposed, you cannot us it any more. Any attempt to use it will result in a `Injector already disposed` error.
After an injector is disposed, you cannot use it anymore. Any attempt to do so will result in an `InjectorDisposedError` error.
The `rootInjector` will never be disposed.
Disposing of your dependencies is always done asynchronously. You should take care to handle this appropriately. The best way to do that is to `await` the result of `myInjector.dispose()`.

@@ -558,3 +576,3 @@

The `tokens` function is a simple helper method that makes sure that an `inject` array is filled with a [tuple type filled with literal strings](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#rest-parameters-with-tuple-types).
The `tokens` function is a simple helper method that makes sure that an `inject` array is filled with a [readonly tuple type filled with literal strings](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#rest-parameters-with-tuple-types). It is mostly there for backward compatibility reasons, since we can now use `as const`, but one might also simply prefer to use `tokens` instead.

@@ -564,7 +582,5 @@ ```ts

// Equivalent to:
const inject: ['foo', 'bar'] = ['foo', 'bar'].
const inject = ['foo', 'bar'] as const;
```
_Note: hopefully [TypeScript will introduce explicit tuple syntax](https://github.com/Microsoft/TypeScript/issues/16656), so this helper method can be removed_
### `InjectableClass<TContext, R, Tokens extends InjectionToken<TContext>[]>`

@@ -616,3 +632,3 @@

constructor(public readonly child: Boom) {}
public static inject = tokens('boom');
public static inject = ['boom'] as const;
}

@@ -645,2 +661,2 @@ try {

Inspiration for the API with static `inject` method comes from years long AngularJS development. Special thanks to the Angular team.
Inspiration for the API with static `inject` method comes from years-long AngularJS development. Special thanks to the Angular team.

@@ -12,4 +12,4 @@ import { InjectionToken, InjectorToken, TargetToken } from './InjectionToken';

export type CorrespondingTypes<TContext, TS extends InjectionToken<TContext>[]> = {
export type CorrespondingTypes<TContext, TS extends readonly InjectionToken<TContext>[]> = {
[K in keyof TS]: TS[K] extends InjectionToken<TContext> ? CorrespondingType<TContext, TS[K]> : never;
};
export interface Disposable {
dispose(): void | PromiseLike<void>;
}
import { CorrespondingTypes } from './CorrespondingType';
import { InjectionToken } from './InjectionToken';
export type InjectableClass<TContext, R, Tokens extends InjectionToken<TContext>[]> =
export type InjectableClass<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> =
| ClassWithInjections<TContext, R, Tokens>
| ClassWithoutInjections<R>;
export interface ClassWithInjections<TContext, R, Tokens extends InjectionToken<TContext>[]> {
export interface ClassWithInjections<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> {
new (...args: CorrespondingTypes<TContext, Tokens>): R;

@@ -15,7 +15,7 @@ readonly inject: Tokens;

export type InjectableFunction<TContext, R, Tokens extends InjectionToken<TContext>[]> =
export type InjectableFunction<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> =
| InjectableFunctionWithInject<TContext, R, Tokens>
| InjectableFunctionWithoutInject<R>;
export interface InjectableFunctionWithInject<TContext, R, Tokens extends InjectionToken<TContext>[]> {
export interface InjectableFunctionWithInject<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> {
(...args: CorrespondingTypes<TContext, Tokens>): R;

@@ -27,4 +27,4 @@ readonly inject: Tokens;

export type Injectable<TContext, R, Tokens extends InjectionToken<TContext>[]> =
export type Injectable<TContext, R, Tokens extends readonly InjectionToken<TContext>[]> =
| InjectableClass<TContext, R, Tokens>
| InjectableFunction<TContext, R, Tokens>;
export type InjectionTarget = Function | symbol | number | string | undefined;

@@ -0,0 +0,0 @@ export type InjectorToken = '$injector';

@@ -7,7 +7,7 @@ import { InjectableClass, InjectableFunction } from './Injectable';

export interface Injector<TContext = {}> {
injectClass<R, Tokens extends InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>): R;
injectFunction<R, Tokens extends InjectionToken<TContext>[]>(Class: InjectableFunction<TContext, R, Tokens>): R;
injectClass<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>): R;
injectFunction<R, Tokens extends readonly InjectionToken<TContext>[]>(Class: InjectableFunction<TContext, R, Tokens>): R;
resolve<Token extends keyof TContext>(token: Token): TContext[Token];
provideValue<Token extends string, R>(token: Token, value: R): Injector<TChildContext<TContext, R, Token>>;
provideClass<Token extends string, R, Tokens extends InjectionToken<TContext>[]>(
provideClass<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(
token: Token,

@@ -17,3 +17,3 @@ Class: InjectableClass<TContext, R, Tokens>,

): Injector<TChildContext<TContext, R, Token>>;
provideFactory<Token extends string, R, Tokens extends InjectionToken<TContext>[]>(
provideFactory<Token extends string, R, Tokens extends readonly InjectionToken<TContext>[]>(
token: Token,

@@ -20,0 +20,0 @@ factory: InjectableFunction<TContext, R, Tokens>,

export enum Scope {
Transient = 'transient',
Singleton = 'singleton'
Singleton = 'singleton',
}

@@ -0,0 +0,0 @@ export type TChildContext<TParentContext, TProvided, CurrentToken extends string> = {

@@ -50,3 +50,3 @@ import { InjectionTarget } from './api/InjectionTarget';

static create(target: InjectionTarget, error: Error) {
static create(target: InjectionTarget, error: Error): InjectionError {
if (error instanceof InjectionError) {

@@ -53,0 +53,0 @@ return new InjectionError([target, ...error.path], error.cause);

@@ -0,0 +0,0 @@ export * from './api/Injectable';

@@ -0,1 +1,4 @@

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { Scope } from './api/Scope';

@@ -36,2 +39,4 @@ import { InjectionToken, INJECTOR_TOKEN, TARGET_TOKEN } from './api/InjectionToken';

abstract class AbstractInjector<TContext> implements Injector<TContext> {
private childInjectors: Injector<any>[] = [];
public injectClass<R, Tokens extends InjectionToken<TContext>[]>(Class: InjectableClass<TContext, R, Tokens>, providedIn?: Function): R {

@@ -62,3 +67,3 @@ this.throwIfDisposed(Class);

const tokens: InjectionToken<TContext>[] = (injectable as any).inject || [];
return tokens.map(key => {
return tokens.map((key) => {
switch (key) {

@@ -76,3 +81,6 @@ case TARGET_TOKEN:

public provideValue<Token extends string, R>(token: Token, value: R): AbstractInjector<TChildContext<TContext, R, Token>> {
return new ValueProvider(this, token, value);
this.throwIfDisposed(token);
const provider = new ValueProvider(this, token, value);
this.childInjectors.push(provider as Injector<any>);
return provider;
}

@@ -85,3 +93,6 @@

): AbstractInjector<TChildContext<TContext, R, Token>> {
return new ClassProvider(this, token, scope, Class);
this.throwIfDisposed(token);
const provider = new ClassProvider(this, token, scope, Class);
this.childInjectors.push(provider as Injector<any>);
return provider;
}

@@ -93,3 +104,6 @@ public provideFactory<Token extends string, R, Tokens extends InjectionToken<TContext>[]>(

): AbstractInjector<TChildContext<TContext, R, Token>> {
return new FactoryProvider(this, token, scope, factory);
this.throwIfDisposed(token);
const provider = new FactoryProvider(this, token, scope, factory);
this.childInjectors.push(provider as Injector<any>);
return provider;
}

@@ -108,6 +122,15 @@

protected abstract get isDisposed(): boolean;
private isDisposed = false;
public abstract dispose(): Promise<void>;
public async dispose() {
if (!this.isDisposed) {
this.isDisposed = true; // be sure new disposables aren't added while we're disposing
await Promise.all(this.childInjectors.map((child) => child.dispose()));
while (this.childInjectors.pop()); // Don't keep the references, might cause a memory leak
await this.disposeInjectedValues();
}
}
protected abstract disposeInjectedValues(): Promise<void>;
protected abstract resolveInternal<Token extends keyof TContext>(token: Token, target?: Function): TContext[Token];

@@ -120,8 +143,5 @@ }

}
public dispose() {
protected disposeInjectedValues() {
return Promise.resolve();
}
get isDisposed() {
return false;
}
}

@@ -141,15 +161,4 @@

protected isDisposed = false;
public async dispose() {
if (!this.isDisposed) {
this.isDisposed = true; // be sure new disposables aren't added while we're disposing
await this.disposeInjectedValues();
this.disposables.clear();
await this.parent.dispose();
}
}
private async disposeInjectedValues() {
const promisesToAwait = [...this.disposables.values()].map(disposable => disposable.dispose());
protected async disposeInjectedValues() {
const promisesToAwait = [...this.disposables.values()].map((disposable) => disposable.dispose());
await Promise.all(promisesToAwait);

@@ -238,2 +247,4 @@ }

export const rootInjector: Injector<{}> = new RootInjector();
export function createInjector(): Injector<{}> {
return new RootInjector();
}

@@ -10,4 +10,4 @@ /**

*/
export function tokens<TS extends string[]>(...tokens: TS): TS {
export function tokens<TS extends readonly string[]>(...tokens: TS): TS {
return tokens;
}

@@ -0,0 +0,0 @@ {

import { Disposable } from './api/Disposable';
export function isDisposable(maybeDisposable: any): maybeDisposable is Disposable {
return maybeDisposable && maybeDisposable.dispose && typeof maybeDisposable.dispose === 'function';
export function isDisposable(maybeDisposable: unknown): maybeDisposable is Disposable {
const asDisposable = maybeDisposable as Disposable;
return asDisposable && asDisposable.dispose && typeof asDisposable.dispose === 'function';
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc