typestack / typedi

Simple yet powerful dependency injection tool for JavaScript and TypeScript.
https://docs.typestack.community/typedi/v/develop/01-getting-started
MIT License
4.06k stars 169 forks source link

fix: not working function dependency injection examples from docs #813

Open sergiycheck opened 2 years ago

sergiycheck commented 2 years ago

Description

I'm reading docs here github docs and I find that function dependency injection examples does not work and Service decorator does not accept function arg.

Minimal code-snippet showcasing the problem

export const PostRepository = Service(() => ({
  getName() {
    return 'hello from post repository';
  },
}));

Expected behavior

Actual behavior

image

typedi version

{
 "typedi": "^0.10.0"
}
maxbol commented 1 year ago

+1 on this, any fix in sight?

attilaorosz commented 1 year ago

I might be missing something here but the docs says you have to provide your factory function in the factory property.

maxbol commented 1 year ago

Seems like this feature was removed in favor of Container.set({ factory }):

https://github.com/typestack/typedi/commit/9228bfaa6c91a7b28d464a9bdcec63fb94d6079f

If anyone is interested, I was quite easily able to recreate it. Using mapped tuple types from ts 3.1 I was also able to simplify the types quite a bit:

import Container, { ContainerInstance, ServiceIdentifier, Token } from "typedi";

export type WithDependenciesFactoryArgs<Deps extends ServiceIdentifier<any>[]> =
  {
    [K in keyof Deps]: WithDependenciesResolvedDep<Deps[K]>;
  };

export type WithDependenciesFactory<
  ServiceDependencies extends ServiceIdentifier<any>[],
  ServiceType,
> = (...args: WithDependenciesFactoryArgs<ServiceDependencies>) => ServiceType;

export type WithDependenciesResolvedDep<Identifier> =
  Identifier extends ServiceIdentifier<infer T> ? T : never;

export function withDependenciesFactory<
  ServiceDependencies extends ServiceIdentifier<any>[],
  ServiceType,
>(
  factory: WithDependenciesFactory<ServiceDependencies, ServiceType>,
  ...dependencies: ServiceDependencies
) {
  const serviceId = new Token<ServiceType>();

  Container.set({
    eager: false,
    factory: [
      class DefaultFactory {
        create(container: ContainerInstance) {
          const params = dependencies.map((dependency) => {
            return container.get(dependency);
          }) as WithDependenciesFactoryArgs<ServiceDependencies>;
          return factory(...params);
        }
      },
      "create",
    ],
    global: false,
    id: serviceId,
    multiple: false,
    transient: false,
  });

  return serviceId;
}

To use:

const MyService = withDependenciesFactory(
   (depA, depB, depC) => {
      return {
         sum: () => depA.count() + depB.count() + depC.count()
      }
   },
   DependencyA,
   DependencyB,
   DependencyC
);

const myService = Container.get(MyService);

myService.sum();