Code Monkey home page Code Monkey logo

typescript-injector-factory's Introduction

Typescript Injector Factory (WIP)

This library aims building custom injectors for instance fields, constructor and method parameters.

Usage

import { createInjectorFactory } from "./injectors/injectorFactory";

type Context = Record<string, unknown>; // The context can be any type
const factory = createInjectorFactory<Context>();

const ExtractValue = factory.createInjector(
	/*
	 * The extra parameters after the payload argument can be custom and the resulting
	 * curried decorator will require them
	 */
	(payload, key: string, options: { required: boolean }) => {
		const context: Context = payload.context;
		const value = context[key];
		if (!value && options.required) {
			throw new Error(`${key} is required!`);
		}
		return value;
	}
);


// This produces a decorator generator in the form:
@ExtractValue(key: string, options: {required: boolean})

This can be used as a decorator on consturctor parameters, method parameters and fields Multiple decorators can be created from the same factory

const AccountValue = factory.createInjector((payload) => {
	const context: Context = payload.context.account;
});
class SampleClass {
	@AccountValue()
	private account: number;

	public constructor(
		@ExtractValue("name", { required: true }) private name: string,
		@ExtractValue("lastName", { required: true }) private lastName: string
	) {
			console.log(`First name: ${this.name} - Last name: ${this.lastName}`):
	}

	public someMethod(@ExtractValue("age", { required: false }) age: number) {
		console.log(`Age is ${age}`);
	}

	public logAccount() {
		console.log(`Account value is ${this.account}`);
	}
}

To construct the instances and call its methods we have to provide a concrete context

const concreteContext : Context = { name: "John", lastName: "Doe", age: 23, ... };
const someOtherContext: Context = { account: 100 };

// Construction
const instance: SampleClass = factory.with(concreteContext).construct(SampleClass);
// First name: John - Last name: Doe

// Method execution
factory.with(concreteContext).call(instance, "someMethod");
// Age is 23

// Method execution with another context
factory.with(someOtherContext).call(new SampleClass("John", "Doe"), "logAcount");
// Account value is 100

We can also provice a default injector if none is specified for any parameter If no injector is found and the default injector is not given it will throw an error

const factory = createInjectorFactory<Context>((payload) => undefined);

Async injectors

Async injectors work the same way but to invoke them you have to use constructAsync(...) and callAsync(...) instead

import { createInjectorFactory } from "./injectors/injectorFactory";
type Context = Record<string, unknown>; // The context can be any type
const factory = createInjectorFactory<Context>();

const CurrentUser = factory.createInjector(async ({context})=>{
	await fetchUserFromSource(...)
});

const DbConnection = factory.createInjector(async ({context})=>{
	await connectToDabase(...);
});

class Service {
	public constructor(@DbConnection() private dbConnection: Connection) {
	}

	public getProfile(@CurrentUser() user: User) {
		return user;
	}
}

async function main() {
	const service = factory.construct()
	const resolver = factory.with({...});
	const service: Service = await resolver.constructAsync(Service);
	const user = await resolver.callAsync(service, "getProfile");
	console.log(user);
}

main();

Examples

Express example

import { createInjectorFactory } from "../src";
import express from "express";

const app = express();

const factory = createInjectorFactory<express.Request>();

const headers = factory.createInjector((payload) => {
	return payload.context.headers;
});

class SimpleHandler {
	async handle(
		@headers() headers: Record<string, string | string[]>
	): Promise<Record<string, string | string[]>> {
		return headers;
	}
}

const handler = new SimpleHandler();

app.use("*", async (request, response) => {
	const result: string = await factory.with(request).call(handler, "handle");
	response.send(result);
});

app.listen(8080, "0.0.0.0", () => {
	console.log(`Listening to 8080`);
});

TODO:

  • Async resolvers
  • Unit tests
  • Fix types in the API
  • Fix types in the internal implemenation
  • Typecheck and/or Throw error when calling a non-existent method

typescript-injector-factory's People

Contributors

elis-vathi avatar

Stargazers

Elis Vathi avatar

Watchers

Elis Vathi avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.