Last active
August 25, 2023 15:23
-
-
Save infinite-system/12269d96f06b7abe9ad050ec85838bbe to your computer and use it in GitHub Desktop.
Basic IOC container similar to Inversify
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
class Mapping { | |
constructor (public from: ObjectConstructor) { | |
this.to = from | |
} | |
to: ObjectConstructor | |
scope = 'singleton' | |
onActivation: (resolved, ...args) => {} | |
} | |
export class Kernel { | |
mappings = new Map() | |
instances = new Map() | |
current: Mapping | |
bind (from) { | |
const mapping = new Mapping(from) | |
this.current = mapping | |
this.mappings.set(from, mapping) | |
return this | |
} | |
to (to) { | |
this.current.to = to | |
return this | |
} | |
transient () { | |
this.current.scope = 'transient' | |
return this | |
} | |
singleton () { | |
this.current.scope = 'singleton' | |
return this | |
} | |
onActivation (callback) { | |
this.current.onActivation = callback | |
return this | |
} | |
} | |
export const kernel = new Kernel() | |
export function get<T extends abstract new (...args: any) => any> ( | |
className: T, | |
...args: T extends { new (...args: infer P): any } ? P : never[] | |
): any { | |
const mapping = kernel.mappings.get(className) | |
if (!mapping) { | |
const constructorName = className?.constructor?.name || String(className) | |
throw new Error('Kernel mapping does not exist for ' + constructorName) | |
} | |
// Handle singleton | |
if (mapping.scope === 'singleton') { | |
// Instance exists? Return it. | |
if (kernel.instances.has(className)) | |
return kernel.instances.get(className) | |
// Instance does not exist? Create it. | |
const newInstance = mapping.onActivation | |
? mapping.onActivation(mapping.to, ...args) | |
: new mapping.to(...args) | |
// Save instance. | |
kernel.instances.set(className, newInstance) | |
return newInstance | |
} else { | |
// Handle transient | |
return mapping.onActivation | |
? mapping.onActivation(mapping.to, ...args) | |
: new mapping.to(...args) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@fatso83
Hey Carl-Erik!
This was just an initial draft of the Kernel, there is a new version as part of my vue integration package, you can take a look here:
https://github.com/infinite-system/infinite-vue/blob/main/src/kernel.ts
It's a bit different from the version here, the singletons and transients in the example gist share the same namespace, in the newer version in the link, the singletons and transients have their own scope, so you can define:
So I changed the design a bit, that I have 2 functions to use the container(get, make) rather than 1 to make it explicit whether you are using a singleton or a transient.
The main difference is my api is very succinct, most frameworks api for this is very verbose and I don't like it, too much typing involved, as well as doesn't fully suit my needs.
These are just examples, so you can build your own, it's not that difficult if you follow the right concepts.
I am not sure how it fully compares to others, but I get full typescript support in my approach, especially the updated version in the link.
Also my method supports auto-binding.
This is useful, so that you do not have to bind anything in startup, unless you want to change the behavior of those classes.