functional-di
functional-di copied to clipboard
Dependency injection in functional style
functional-di
Dependency injection in functional style
Installation
npm install --save functional-di
yarn add functional-di
Usage
deffor creating injectable functionrunfor injecting function
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, number>('ServiceA')
const ServiceB = def<ServiceB>('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<value, arg = void>(name?, defaultHandler?): (arg => value)
def is used for define injectable function
- if
default-handlerwas given, getValue formed like a normal function which can be called directly - otherwise it should not be called without
run(fn)
const getValue = def<number>(
// 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<string, number>('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()
const getValue = def<number>(
// 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