Skip to content

Instantly share code, notes, and snippets.

@temoncher
Created April 6, 2024 12:20
Show Gist options
  • Save temoncher/20cfd76ad53d9666b965087d4f29099d to your computer and use it in GitHub Desktop.
Save temoncher/20cfd76ad53d9666b965087d4f29099d to your computer and use it in GitHub Desktop.
Minimal angular-style injector
let currentInjector: IInjector | null = null;
function runInInjectionContext(injector: IInjector, fn: () => void) {
const prevInjector = currentInjector;
currentInjector = injector;
try {
fn();
} finally {
currentInjector = prevInjector;
}
}
interface IInjector {
get<T>(token: Token<T>): T;
register<T>(token: Token<T>, implementation: T): void;
}
class Injector implements IInjector {
#container = new Map<Symbol, any>();
get<T>(token: Token<T>): T {
return this.#container.get(token)
}
register<T>(token: Token<T>, implementation: T) {
return this.#container.set(token, implementation);
}
}
function inject<T>(token: Token<T>): T {
if (!currentInjector) throw new Error("inject should be used inside an injection context");
const dep = currentInjector.get(token);
if (!dep) throw new Error(`Dependency for token ${Symbol.keyFor(token)} not found`);
return dep;
}
type Token<T> = symbol & { __tokenType: T };
function createToken<T>(tokenString: string): Token<T> {
return Symbol(tokenString) as any;
}
interface ILogger {
log(str: string): void;
}
const LOGGER_TOKEN = createToken<ILogger>("Logger");
class ConsoleLogger implements ILogger {
log(str: string) {
console.log(str);
}
}
const sleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay))
class Awaiter {
logger = inject(LOGGER_TOKEN);
async logAndWait() {
this.logger.log("starting");
await sleep(5000);
this.logger.log("finished")
}
}
function main() {
const injector = new Injector();
injector.register(LOGGER_TOKEN, new ConsoleLogger());
runInInjectionContext(injector, () => {
const awaiter = new Awaiter();
awaiter.logAndWait();
});
}
main();let currentInjector: IInjector | null = null;
function runInInjectionContext(injector: IInjector, fn: () => void) {
const prevInjector = currentInjector;
currentInjector = injector;
try {
fn();
} finally {
currentInjector = prevInjector;
}
}
interface IInjector {
get<T>(token: Token<T>): T;
register<T>(token: Token<T>, implementation: T): void;
}
class Injector implements IInjector {
#container = new Map<Symbol, any>();
get<T>(token: Token<T>): T {
return this.#container.get(token)
}
register<T>(token: Token<T>, implementation: T) {
return this.#container.set(token, implementation);
}
}
function inject<T>(token: Token<T>): T {
if (!currentInjector) throw new Error("inject should be used inside an injection context");
const dep = currentInjector.get(token);
if (!dep) throw new Error(`Dependency for token ${Symbol.keyFor(token)} not found`);
return dep;
}
type Token<T> = symbol & { __tokenType: T };
function createToken<T>(tokenString: string): Token<T> {
return Symbol(tokenString) as any;
}
interface ILogger {
log(str: string): void;
}
const LOGGER_TOKEN = createToken<ILogger>("Logger");
class ConsoleLogger implements ILogger {
log(str: string) {
console.log(str);
}
}
const sleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay))
class Awaiter {
logger = inject(LOGGER_TOKEN);
async logAndWait() {
this.logger.log("starting");
await sleep(5000);
this.logger.log("finished")
}
}
function main() {
const injector = new Injector();
injector.register(LOGGER_TOKEN, new ConsoleLogger());
runInInjectionContext(injector, () => {
const awaiter = new Awaiter();
awaiter.logAndWait();
});
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment