Integrating nodejs DI container awilix with type safety

Viewed 863

I am looking at integrating DI container in to one of my existing nodejs projects. I have integrated awilix and things are working as expected.

However, I am used to typescript and use the type safety in many parts. This is one thing I am not able to get with registering the dependencies using awilix.

e.g. I write the use cases like higher order function

function createReport(specs){

  const {reportRepostiory} = specs;

  return async (param1: string, param2: string){
    //...
    reportRepostiory.create({//some payload})
  }
  
}

The calling function calls like


const reportService : any = container.resolve("createReport");
const result = await reportService("1", "2")

The above code works fine with proper container config. However, there is no type inference on the resolved function object. Is there any way to get the types?

1 Answers

This is not my own original answer, but I came across this code-sandbox that might help you: https://codesandbox.io/s/qykt1?file=/src/index.ts.

In case the link dies, here is the snippet extracted from the above link.

All credit goes to the author, derekrjones (https://codesandbox.io/u/derekrjones).

import {
  AwilixContainer,
  asFunction,
  asValue,
  asClass,
  InjectionMode,
  createContainer,
  Resolver,
  ResolveOptions,
  ContainerOptions
} from "awilix";

/**
 * Container definition base.
 */
interface ContainerDefinition {
  [key: string]: Resolver<unknown>;
}

/**
 * Extracts the type that will be resolved from a resolver.
 */
type ExtractResolverType<T> = T extends Resolver<infer X> ? X : null;

/**
 * Strongly-typed container.
 */
interface TypedAwilixContainer<T extends ContainerDefinition>
  extends Pick<AwilixContainer, Exclude<keyof AwilixContainer, "resolve">> {
  /**
   * Resolves the registration with the given name.
   *
   * @param  {string} name
   * The name of the registration to resolve.
   *
   * @return {*}
   * Whatever was resolved.
   */
  resolve<K extends keyof T>(
    key: K,
    resolveOptions?: ResolveOptions
  ): ExtractResolverType<T[K]>;
}

/**
 * Wraps `createContainer` and calls `register` on it.
 */
function createTypedContainer<T extends ContainerDefinition>(
  registrations: T,
  opts?: ContainerOptions
): TypedAwilixContainer<T> {
  return createContainer(opts).register(registrations) as any;
}
Related