Skip to content

Commit ae83ecb

Browse files
authored
v0.2.1 (#26)
* Change behaviour of `IContainerBuilder.define` so that already-defined services are not overwritten * Refactor Container.get * Update README
1 parent 83893af commit ae83ecb

File tree

13 files changed

+125
-26
lines changed

13 files changed

+125
-26
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [0.2.1]
4+
5+
- Change behaviour of `IContainerBuilder.define` so that already-defined services are not overwritten
6+
37
## [0.1.16]
48

59
- Fix type signature of `IContainerBuilder.use` method

README.md

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ These capabilities are implemented by components of the application, with some c
4141

4242
## Usage
4343

44+
### Creating a container module and defining services
45+
4446
Take the example of a logging component, that defines services in a container using the following keys (see [./examples/container/loggingModule/keys.ts](https://github.com/mgdigital/tsinject/blob/main/examples/container/loggingModule/keys.ts)):
4547

4648
```typescript
@@ -138,12 +140,77 @@ const logger = container.get(loggingModule.keys.logger)
138140
logger.info('Logging something!')
139141
```
140142

141-
Note that we should only call [IContainer.get](https://mgdigital.github.io/tsinject/interfaces/IContainer.html#get) from within a factory function or from the [composition root](https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/), avoiding the [service locator anti-pattern](https://freecontent.manning.com/the-service-locator-anti-pattern/).
143+
**Note:** We should only call [IContainer.get](https://mgdigital.github.io/tsinject/interfaces/IContainer.html#get) from within a factory function or from the [composition root](https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/), avoiding the [service locator anti-pattern](https://freecontent.manning.com/the-service-locator-anti-pattern/).
144+
145+
**Note:** When defining a service in the container, if that service key is already defined then the key will **not** be overwritten. This allows modules to be used multiple times without introducing unpredictable behaviour when using decorators. For example of module A and B both depend on module C they can both use module C and then be used together in the same container. If an already defined service needs to be overwritten, this can be done with a decorator.
146+
147+
## Decorators
148+
149+
Decorators allow us to modify an already-defined service. Let's create a custom logging module that decorates some of the services in the base module defined above:
150+
151+
152+
```typescript
153+
import type { ContainerModule } from '@mgdigital/tsinject'
154+
import loggingModule from './examples/container/loggingModule'
155+
156+
const myCustomLoggingModule: ContainerModule<
157+
loggingModule.services
158+
> = builder => builder
159+
.use(loggingModule.default)
160+
// Decorate the logger config so that output is always pretty
161+
.decorate(
162+
loggingModule.keys.loggerConfig,
163+
factory => container => ({
164+
...factory(container),
165+
pretty: true
166+
})
167+
)
168+
// Overwrite the log writer with some other implementation
169+
.decorate(
170+
loggingModule.keys.logWriter,
171+
() => () => myCustomLogWriter
172+
)
173+
```
174+
175+
We can also use decorators to achieve features that aren't explicitly implemented in this library, such as service tagging, which we can do by defining a service as an array:
176+
177+
```typescript
178+
import type { ContainerModule } from '@mgdigital/tsinject'
179+
180+
type TaggedServiceType = { foo: string }
181+
182+
const serviceTag = Symbol('serviceTag')
183+
184+
type ServiceMap = {
185+
[serviceTag]: TaggedServiceType[]
186+
}
187+
188+
const myModule: ContainerModule<
189+
ServiceMap
190+
> = builder => builder
191+
.define(
192+
serviceTag,
193+
() => []
194+
)
195+
196+
const myOtherModule: ContainerModule<
197+
ServiceMap
198+
> = builder => builder
199+
.use(myModule)
200+
.decorate(
201+
serviceTag,
202+
// Add a service to the array of already defined services
203+
factory => container => [
204+
...factory(container),
205+
{ foo: 'bar' }
206+
]
207+
)
208+
```
142209

143210
And that's it - unlike some other DI containers that claim to be lightweight, tsinject really is tiny and has a simple API, allowing large and complex but loosely coupled applications to be built from small, simple and easily testable components.
144211

145212
See the [examples](https://github.com/mgdigital/tsinject/tree/main/examples) folder for a more complete application. It includes a simple tasks service with a REST API that can be started by cloning this repository and running `yarn install`, `yarn build` then `yarn example:start`.
146213

147214
---
148215

149-
Copyright (c) 2021 Mike Gibson, https://github.com/mgdigital.
216+
Copyright (c) 2021 Mike Gibson, https://github.com/mgdigital

docs/classes/ServiceNotFoundError.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html><html class="default no-js"><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>ServiceNotFoundError | @mgdigital/tsinject - v0.1.16</title><meta name="description" content="Documentation for @mgdigital/tsinject - v0.1.16"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script async src="../assets/search.js" id="search-script"></script></head><body><script>document.body.classList.add(localStorage.getItem(&quot;tsd-theme&quot;) || &quot;os&quot;)</script><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base=".."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">@mgdigital/tsinject - v0.1.16</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label><input type="checkbox" id="tsd-filter-externals" checked/><label class="tsd-widget" for="tsd-filter-externals">Externals</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><ul class="tsd-breadcrumb"><li><a href="../modules.html">@mgdigital/tsinject - v0.1.16</a></li><li><a href="ServiceNotFoundError.html">ServiceNotFoundError</a></li></ul><h1>Class ServiceNotFoundError</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><section class="tsd-panel tsd-comment"><div class="tsd-comment tsd-typography"><div class="lead">
1+
<!DOCTYPE html><html class="default no-js"><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>ServiceNotFoundError | @mgdigital/tsinject - v0.2.1</title><meta name="description" content="Documentation for @mgdigital/tsinject - v0.2.1"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script async src="../assets/search.js" id="search-script"></script></head><body><script>document.body.classList.add(localStorage.getItem(&quot;tsd-theme&quot;) || &quot;os&quot;)</script><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base=".."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">@mgdigital/tsinject - v0.2.1</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label><input type="checkbox" id="tsd-filter-externals" checked/><label class="tsd-widget" for="tsd-filter-externals">Externals</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><ul class="tsd-breadcrumb"><li><a href="../modules.html">@mgdigital/tsinject - v0.2.1</a></li><li><a href="ServiceNotFoundError.html">ServiceNotFoundError</a></li></ul><h1>Class ServiceNotFoundError</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><section class="tsd-panel tsd-comment"><div class="tsd-comment tsd-typography"><div class="lead">
22
<p>Error thrown when a non-existent key is requested from the container.</p>
33
</div></div></section><section class="tsd-panel tsd-hierarchy"><h3>Hierarchy</h3><ul class="tsd-hierarchy"><li><a href="TSInjectError.html" class="tsd-signature-type" data-tsd-kind="Class">TSInjectError</a><ul class="tsd-hierarchy"><li><span class="target">ServiceNotFoundError</span></li></ul></li></ul></section><section class="tsd-panel-group tsd-index-group"><h2>Index</h2><section class="tsd-panel tsd-index-panel"><div class="tsd-index-content"><section class="tsd-index-section "><h3>Constructors</h3><ul class="tsd-index-list"><li class="tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite"><a href="ServiceNotFoundError.html#constructor" class="tsd-kind-icon">constructor</a></li></ul></section><section class="tsd-index-section "><h3>Properties</h3><ul class="tsd-index-list"><li class="tsd-kind-property tsd-parent-kind-class"><a href="ServiceNotFoundError.html#key" class="tsd-kind-icon">key</a></li><li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a href="ServiceNotFoundError.html#message" class="tsd-kind-icon">message</a></li><li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a href="ServiceNotFoundError.html#name" class="tsd-kind-icon">name</a></li><li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a href="ServiceNotFoundError.html#stack" class="tsd-kind-icon">stack</a></li><li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-static tsd-is-external"><a href="ServiceNotFoundError.html#prepareStackTrace" class="tsd-kind-icon">prepare<wbr/>Stack<wbr/>Trace</a></li><li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-static tsd-is-external"><a href="ServiceNotFoundError.html#stackTraceLimit" class="tsd-kind-icon">stack<wbr/>Trace<wbr/>Limit</a></li></ul></section><section class="tsd-index-section tsd-is-inherited tsd-is-external"><h3>Methods</h3><ul class="tsd-index-list"><li class="tsd-kind-method tsd-parent-kind-class tsd-is-inherited tsd-is-static tsd-is-external"><a href="ServiceNotFoundError.html#captureStackTrace" class="tsd-kind-icon">capture<wbr/>Stack<wbr/>Trace</a></li></ul></section></div></section></section><section class="tsd-panel-group tsd-member-group "><h2>Constructors</h2><section class="tsd-panel tsd-member tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite"><a id="constructor" class="tsd-anchor"></a><h3>constructor</h3><ul class="tsd-signatures tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite"><li class="tsd-signature tsd-kind-icon">new <wbr/>Service<wbr/>Not<wbr/>Found<wbr/>Error<span class="tsd-signature-symbol">(</span>key<span class="tsd-signature-symbol">: </span><a href="../modules.html#ContainerKey" class="tsd-signature-type" data-tsd-kind="Type alias">ContainerKey</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><a href="ServiceNotFoundError.html" class="tsd-signature-type" data-tsd-kind="Class">ServiceNotFoundError</a></li></ul><ul class="tsd-descriptions"><li class="tsd-description"><aside class="tsd-sources"><p>Overrides <a href="TSInjectError.html">TSInjectError</a>.<a href="TSInjectError.html#constructor">constructor</a></p><ul><li>Defined in <a href="https://github.com/mgdigital/tsinject/blob/main/src/errors.ts#L12">Code/tsinject/src/errors.ts:12</a></li></ul></aside><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameters"><li><h5>key: <a href="../modules.html#ContainerKey" class="tsd-signature-type" data-tsd-kind="Type alias">ContainerKey</a></h5></li></ul><h4 class="tsd-returns-title">Returns <a href="ServiceNotFoundError.html" class="tsd-signature-type" data-tsd-kind="Class">ServiceNotFoundError</a></h4></li></ul></section></section><section class="tsd-panel-group tsd-member-group "><h2>Properties</h2><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-class"><a id="key" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagReadonly">Readonly</span> key</h3><div class="tsd-signature tsd-kind-icon">key<span class="tsd-signature-symbol">:</span> <a href="../modules.html#ContainerKey" class="tsd-signature-type" data-tsd-kind="Type alias">ContainerKey</a></div><aside class="tsd-sources"></aside></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a id="message" class="tsd-anchor"></a><h3>message</h3><div class="tsd-signature tsd-kind-icon">message<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><p>Inherited from <a href="TSInjectError.html">TSInjectError</a>.<a href="TSInjectError.html#message">message</a></p><ul><li>Defined in .yarn/berry/cache/typescript-patch-29eb8bf885-8.zip/node_modules/typescript/lib/lib.es5.d.ts:974</li></ul></aside></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a id="name" class="tsd-anchor"></a><h3>name</h3><div class="tsd-signature tsd-kind-icon">name<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><p>Inherited from <a href="TSInjectError.html">TSInjectError</a>.<a href="TSInjectError.html#name">name</a></p><ul><li>Defined in .yarn/berry/cache/typescript-patch-29eb8bf885-8.zip/node_modules/typescript/lib/lib.es5.d.ts:973</li></ul></aside></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-external"><a id="stack" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> stack</h3><div class="tsd-signature tsd-kind-icon">stack<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources"><p>Inherited from <a href="TSInjectError.html">TSInjectError</a>.<a href="TSInjectError.html#stack">stack</a></p><ul><li>Defined in .yarn/berry/cache/typescript-patch-29eb8bf885-8.zip/node_modules/typescript/lib/lib.es5.d.ts:975</li></ul></aside></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-static tsd-is-external"><a id="prepareStackTrace" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagStatic">Static</span> <span class="tsd-flag ts-flagOptional">Optional</span> prepare<wbr/>Stack<wbr/>Trace</h3><div class="tsd-signature tsd-kind-icon">prepare<wbr/>Stack<wbr/>Trace<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-symbol">(</span>err<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Error</span>, stackTraces<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">CallSite</span><span class="tsd-signature-symbol">[]</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol"> =&gt; </span><span class="tsd-signature-type">any</span></div><aside class="tsd-sources"><p>Inherited from <a href="TSInjectError.html">TSInjectError</a>.<a href="TSInjectError.html#prepareStackTrace">prepareStackTrace</a></p><ul><li>Defined in .yarn/berry/cache/@types-node-npm-16.11.0-75617d0fee-8.zip/node_modules/@types/node/globals.d.ts:11</li></ul></aside><div class="tsd-type-declaration"><h4>Type declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures tsd-kind-type-literal tsd-parent-kind-class"><li class="tsd-signature tsd-kind-icon"><span class="tsd-signature-symbol">(</span>err<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Error</span>, stackTraces<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">CallSite</span><span class="tsd-signature-symbol">[]</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">any</span></li></ul><ul class="tsd-descriptions"><li class="tsd-description"><div class="tsd-comment tsd-typography"><div class="lead">
44
<p>Optional override for formatting stack traces</p>

0 commit comments

Comments
 (0)