Last active
August 11, 2023 07:56
-
-
Save peatiscoding/ee924c3793788b7a21c56ef9a73547c8 to your computer and use it in GitHub Desktop.
Modularized Dependency Injection using JavaScript's Proxy Object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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