vovaspace / brandi

The dependency injection container powered by TypeScript.
https://brandi.js.org
ISC License
193 stars 12 forks source link

Unable to get instance at runtime - what am i missing? #22

Closed gdwais closed 2 years ago

gdwais commented 2 years ago

I'm trying to implement this library and I'm having trouble getting an instance at runtime when the constructor requires a dependency. Constants and instances (that don't have dependencies) work fine. What am I doing wrong?!

Here is the stack it throws:

/Users/merlin/Development/app/server/node_modules/brandi/lib/brandi.js:416
    if (target.length === 0)
               ^

TypeError: Cannot read properties of undefined (reading 'length')
    at Container.getParameters (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:416:16)
    at Container.createInstance (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:397:29)
    at Container.resolveCache (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:392:27)
    at Container.resolveBinding (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:369:21)
    at Container.resolveToken (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:356:19)
    at Container.get (/Users/merlin/app/server/node_modules/brandi/lib/brandi.js:348:17)
    at Object.<anonymous> (/Users/merlin/app/server/dist/routes/assets.js:12:42)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Here is my implementation:

import { PrismaClient } from "@prisma/client";
import { token } from "brandi";
import { AssetRepository } from "../repositories";
import { TimingService } from "../services";

const TOKENS = {
    prismaClient: token<PrismaClient>("prismaClient"),
    assetRepository: token<AssetRepository>("assetRepository"),
    timingService: token<TimingService>("timingService")
}

const container = new Container();

container.bind(TOKENS.prismaClient).toConstant(new PrismaClient());
container.bind(TOKENS.assetRepository).toInstance(AssetRepository).inContainerScope();
container.bind(TOKENS.timingService).toInstance(TimingService).inSingletonScope();

My class

// this one breaks when I try to instantiate
export class AssetRepository {
    db: PrismaClient;

    constructor(prismaClient: PrismaClient) {
        this.db = prismaClient;
    }

// a bunch of other methods
}

injected(AssetRepository, TOKENS.prismaClient);

pulling it out of the container:

// this works
const db = container.get(TOKENS.prismaClient);

// breaks here
const assetRepository = container.get(TOKENS.assetRepository)
vovaspace commented 2 years ago

Hi!

There seems to be a cyclic dependence between file with container and TOKENS and file with AssetRepository. Let's try to move TOKENS to separate file and use import type syntax:

/** AssetRepository.ts */

import { injected } from 'brandi';
import type { PrismaClient } from '@prisma/client';

import { TOKENS } from './tokens';

export class AssetRepository {
  db: PrismaClient;

  constructor(prismaClient: PrismaClient) {
    this.db = prismaClient;
  }

  // a bunch of other methods
}

injected(AssetRepository, TOKENS.prismaClient);
/** tokens.ts */

import { token } from 'brandi';
import type { PrismaClient } from '@prisma/client';

import type { AssetRepository } from './AssetRepository'

export const TOKENS = {
  prismaClient: token<PrismaClient>('PrismaClient'),
  assetRepository: token<AssetRepository>('AssetRepository'),
}
/** container.ts */

import { Container } from 'brandi';
import { PrismaClient } from '@prisma/client'

import { TOKENS } from './tokens';
import { AssetRepository } from './AssetRepository';

export const container = new Container();

container.bind(TOKENS.prismaClient).toConstant(new PrismaClient());
container.bind(TOKENS.assetRepository).toInstance(AssetRepository).inContainerScope();
/** index.ts */

import { container } from './container';
import { TOKENS } from './tokens';

const repository = container.get(TOKENS.assetRepository);

console.log(repository);

I hope this helps.

gdwais commented 2 years ago

same result sadly - I can try and throw together a quick repo that reproduces the issue if that would be helpful

vovaspace commented 2 years ago

It would definitely be helpful.

gdwais commented 2 years ago

https://github.com/gdwais/brandi-sandbox

So I put this together to demonstrate the bug but everything works great! HAHA and now I'm super confused about why i'm having this issue. Either way you can close this. Thank you!