Skip to content

Instantly share code, notes, and snippets.

@peatiscoding
Last active August 11, 2023 07:56
Show Gist options
  • Save peatiscoding/ee924c3793788b7a21c56ef9a73547c8 to your computer and use it in GitHub Desktop.
Save peatiscoding/ee924c3793788b7a21c56ef9a73547c8 to your computer and use it in GitHub Desktop.
Modularized Dependency Injection using JavaScript's Proxy Object
export type InstanceCreator<S, T> = (others: Readonly<S>) => T
export type InstanceCreators<S> = {
[K in keyof S]: InstanceCreator<S, S[K]>
}
/**
* Singleton implementation provider
*
* Example Usage:
*
* ```ts
* interface ServiceShape {
* s3Client: IMediaClient
* s3: IMediaService
* }
*
* const mod = impl<ServiceShape>()
*
* mod.provide('s3Client', () => new S3Client(withEnvironment()))
* mod.provide('s3', (impl: ServiceShape) => new S3MediaService(impl.s3Client))
*
* # upon using
*
* new UploadController(mod.s3) // lazily invoke the call chain above.
* ```
*/
export const impl = <S extends any>() => {
const implementations: Record<string, InstanceCreator<any, any>> = {}
/**
* the function to register the Callback method for creating the
* dependencies
*/
function using<K extends keyof S>(name: K, using: InstanceCreator<S, S[K]>): void
function using(map: InstanceCreators<S>): void
function using<K extends keyof S>(nameOrMap: InstanceCreators<S> | K, using?: InstanceCreator<S, S[K]>): void {
if (typeof nameOrMap === 'string') {
if (!using) {
throw new Error('Invalid usage of impl.using!')
}
implementations[nameOrMap as any] = using!
} else {
const map = nameOrMap as any
for (const [name, using] of Object.entries(map)) {
implementations[name] = using as any
}
}
}
/**
* The instance proxy that can be used to lazily get the instance in singleton manner by its' name
*/
const provider = (): Readonly<S> => {
const _src: Partial<S> = {}
const proxy = new Proxy(_src, {
get(_target, prop, _receiver) {
if (typeof prop !== 'string') {
throw new Error(`Invalid usage, unrecognized prop: ${prop.toString()}`)
}
const key = prop as keyof S
if (!_src[key]) {
if (!implementations[key as string]) {
throw new Error(`Unknown implementation provided for: ${key.toString()}`)
}
_src[key] = implementations[key as string](proxy)
}
return _src[key]
},
}) as Readonly<any>
return proxy
}
return {
using,
provider: provider(),
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment