https://github.com/lucifier129/functional-di
Dependency injection in functional style
https://github.com/lucifier129/functional-di
Last synced: about 1 year ago
JSON representation
Dependency injection in functional style
- Host: GitHub
- URL: https://github.com/lucifier129/functional-di
- Owner: Lucifier129
- License: mit
- Created: 2022-09-21T05:23:37.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2022-09-21T12:25:11.000Z (almost 4 years ago)
- Last Synced: 2025-04-23T12:52:55.687Z (about 1 year ago)
- Language: TypeScript
- Homepage:
- Size: 94.7 KB
- Stars: 19
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# functional-di
[](https://www.npmjs.com/package/functional-di)
[](https://github.com/Lucifier129/functional-di#readme)
[](https://github.com/Lucifier129/functional-di/graphs/commit-activity)
[](https://github.com/Lucifier129/functional-di/blob/master/LICENSE)
[](https://twitter.com/guyingjie129)
> Dependency injection in functional style
### Installation
```sh
npm install --save functional-di
yarn add functional-di
```
### Usage
- `def` for creating injectable function
- `run` for injecting function
```typescript
import { def, run } from 'functional-di'
// define some interfaces
interface ServiceA {
foo(): number
}
interface ServiceB {
foo: string
bar: string
}
// use def to create injectable & callable function
const ServiceA = def('ServiceA')
const ServiceB = def('ServiceB')
// use #impl(handler) to handle request for injected
const serviceAImpl = ServiceA.impl((n) => {
return {
foo() {
return 111 + n
},
}
})
const serviceBImpl = ServiceB.impl(() => {
return {
// call function with argument to make a request
foo: ServiceA(1).foo().toFixed(2),
// different argument is supported
bar: ServiceA(2).foo().toFixed(2),
}
})
const serviceBDirectImpl = ServiceB.impl(() => {
return {
foo: '666',
bar: '777',
}
})
{
const value0 = run(() => ServiceA(1), [serviceAImpl]).foo()
const value1 = run(() => ServiceA(2), [serviceAImpl]).foo()
expect(value0).toEqual(112)
expect(value1).toEqual(113)
}
{
const value = run(() => ServiceB(), [serviceAImpl, serviceBImpl])
expect(value).toEqual({ foo: '112.00', bar: '113.00' })
}
// supports nested
const serverBNestImpl = ServiceB.impl(() => {
const { foo } = run(() => ServiceB(), [serviceAImpl, serviceBImpl])
const { bar } = run(() => ServiceB(), [serviceBDirectImpl])
return {
foo,
bar,
}
})
{
const value = run(() => ServiceB(), [serverBNestImpl])
expect(value).toEqual({ foo: '112.00', bar: '777' })
}
```
## Apis
### def(name?, defaultHandler?): (arg => value)
`def` is used for define injectable function
- if `default-handler` was given, getValue formed like a normal function which can be called directly
- otherwise it should not be called without `run(fn)`
```typescript
const getValue = def(
// the name used for improving readability of the error message
'getValue',
// default-handler when no injected handler found.
() => 0,
)
/**
* if default-handler was given, getValue formed like a normal function which can be called directly
* otherwise it should not be called without `run(fn)`
*/
console.log(getValue()) // 0
/**
* the first type variable is for return type
* the second type variable is for arg type
*/
const getValueWithArg = def('getValueWithArg', (input: number) => `input is : ${input}`)
/**
* give another impl for getValue
* it can be used to inject, replacing the default handler
*/
const getValueImpl = getValue.impl(() => 100)
const getValueWithArgImpl = getValueWithArg.impl((input: number) => {
return `another-impl: input is : ${input}`
})
```
### run(fn, injectedList?): ReturnType
`run` is used for injecting value consumed by the consumers in `fn()`
```typescript
const getValue = def(
// the name used for improving readability of the error message
'getValue',
// default-handler when no injected handler found.
() => 0,
)
console.log(run(() => getValue(), [getValue.impl(() => 1)])) // 1
```
# PRs are welcome