Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/susisu/hokemi
Minimal type-safe dependency injection framework for TypeScript, like Cake Pattern
https://github.com/susisu/hokemi
Last synced: 2 months ago
JSON representation
Minimal type-safe dependency injection framework for TypeScript, like Cake Pattern
- Host: GitHub
- URL: https://github.com/susisu/hokemi
- Owner: susisu
- License: mit
- Created: 2023-05-21T15:20:03.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-09-23T14:29:38.000Z (3 months ago)
- Last Synced: 2024-10-22T06:24:50.552Z (2 months ago)
- Language: TypeScript
- Homepage: https://npmjs.com/package/@susisu/hokemi
- Size: 1.39 MB
- Stars: 14
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# hokemi
[![CI](https://github.com/susisu/hokemi/workflows/CI/badge.svg)](https://github.com/susisu/hokemi/actions?query=workflow%3ACI)
Minimal type-safe dependency injection framework for TypeScript, inspired by Cake Pattern in Scala.
## Installation
``` shell
# npm
npm i --save @susisu/hokemi
# yarn
yarn add @susisu/hokemi
# pnpm
pnpm add @susisu/hokemi
```## Usage
First, declare *components* of your application.
``` ts
import type { Component } from "@susisu/hokemi";export type Clock = {
getTime: () => number;
};
export type ClockComponent = Component<"clock", Clock>;export type Random = {
getRandom: () => number;
};
export type RandomComponent = Component<"random", Random>;export type MyService = {
getTimeAndRandom: () => [number, number];
};
export type MyServiceComponent = Component<"myService", MyService>;
```Each component has a name (e.g. `"clock"` for `ClockComponent`), which is used later to reference its instance.
Next, provide *implementations* for the components.
``` ts
import { impl } from "@susisu/hokemi";export const clockImpl = impl("clock", () => ({
getTime: () => Date.now(),
}));export const randomImpl = impl("random", () => ({
getRandom: () => Math.random(),
}));export const myServiceImpl = impl(
"myService",
({ clock, random }) => ({
getTimeAndRandom: () => [clock.getTime(), random.getRandom()],
})
);
```The `impl` function is a utility function to create an implementation of a component. It takes the component name and a factory function that creates an instance of the component.
`impl` also takes an optional second type argument (e.g. `[ClockComponent, RandomComponent]` for `myServiceImpl`), which specifies the dependencies of the implementation. Dependencies are passed to the factory function when the implementation is instantiated.
Finally, *mix* your implementations and create an instance of the application.
``` ts
import { mixer } from "@susisu/hokemi";const app = mixer(myServiceImpl, clockImpl, randomImpl).new();
console.log(app.myService.getTimeAndRandom()); // => [You can reference each component instance by its name (e.g. `app.myService`).
If you forget to provide some dependencies, or provide mismatched dependencies, it will be detected at compile time with neat error messages.
``` ts
const app = mixer(myServiceImpl, clockImpl).new();
// ~~~
// TS2349: This expression is not callable.
// Type '{ __missingDependenciesError?: { reason: "some dependencies are missing"; providerName: "myService"; dependencies: [{ name: "random"; expectedType: Random; }]; } | undefined; }' has no call signatures.
```### API
#### `Component`
Declares a component.
#### `impl(name, factory)`
Creates an implementation of a component.
`factory` can be either a function or a class that creates an instance of the component.
#### `mixer(...implementations)`
Creates a *mixer* object, which has the following methods:
- `.with(...implementations)`: extends the mixer with more implementations.
- `.new()`: creates a mixed instance.## Troubleshooting
### Cannot export a mixer (TS7056) / type inference for a mixer is too slow
Adding a type annotation will solve the problem.
``` typescript
import { Mixer, mixer } from "@susisu/hokemi";// Don't forget `as const`!
const impls = [myServiceImpl, clockImpl, randomImpl] as const;export const myMixer: Mixer<[...typeof impls]> = mixer(...impls);
```## License
[MIT License](http://opensource.org/licenses/mit-license.php)
## Author
Susisu ([GitHub](https://github.com/susisu), [Twitter](https://twitter.com/susisu2413))