microsoft / tsyringe

Lightweight dependency injection container for JavaScript/TypeScript
MIT License
5.16k stars 173 forks source link

Error injecting interfaces #134

Closed ghost closed 3 years ago

ghost commented 4 years ago

Describe the bug not inject interface

To Reproduce container.register<ITestSubService>(injection_types.ITestSubService, TestSubService);

@injectable()
class LocalStorageService implements ILocalStorageService
{
    constructor(private test : ITestSubService)
    { 
    }
}

console - Uncaught Error: Cannot inject the dependency at position #0 of "LocalStorageService" constructor. Reason: TypeInfo not known for "Object" at eval (dependency-container.js?013a:244) at Array.map () at InternalDependencyContainer.construct (dependency-container.js?013a:229) at InternalDependencyContainer.resolveRegistration (dependency-container.js?013a:108) at InternalDependencyContainer.resolve (dependency-container.js?013a:79) at setup (App.vue?3acc:25) at callWithErrorHandling (runtime-core.esm-bundler.js?5c40:154) at setupStatefulComponent (runtime-core.esm-bundler.js?5c40:6224) at setupComponent (runtime-core.esm-bundler.js?5c40:6185) at mountComponent (runtime-core.esm-bundler.js?5c40:3960) Expected behavior if change ITestSubService to implementation - TestSubService all works fine

Version: 4.3

MeltingMosaic commented 4 years ago

In order to inject interfaces, you will need to use the @inject decorator in the injectable class. The reason is that in TypeScript, interfaces don't really exist at runtime, so we need a different token to resolve the class for you.

Take a look at this section in the readme: https://github.com/microsoft/tsyringe/#inject

ghost commented 4 years ago

If i change to @inject(injection_types.ITestSubService) private test : TestSubService

Uncaught TypeError: Cannot read property 'ITestSubService' of undefined at eval (localstorage.service.ts?e290:24) at Module../src/services/localstorage.service.ts (app.js:1400) at webpack_require (app.js:854) at fn (app.js:151) at eval (injection.config.ts?9941:1) at Module../src/injection/injection.config.ts (app.js:1268) at webpack_require (app.js:854) at fn (app.js:151) at eval (index.ts?7208:1) at Module../src/injection/index.ts (app.js:1256)

in main.ts import "reflect-metadata"; in tsconfig:

 "target": "esnext",
    "module": "ES2020",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,

ts 4.0.3

MeltingMosaic commented 4 years ago

Hmm, can you share the definition of injection-config.ITestSubService? It will need to be a string or Symbol to work.

ghost commented 4 years ago

injection.config.ts


const inject = () =>
{
console.log("Start create inject container....")
container.register<ITestSubService>(injection_types.ITestSubService, TestSubService);
console.log("ITestSubService added...")
// container.register<IApiPathService>(injection_types.IApiPathService,  ApiPathService);
console.log("IApiPathService added...")
container.register<ILocalStorageService>(injection_types.ILocalStorageService, LocalStorageService);
console.log("ILocalStorageService added...")
container.registerSingleton<IAuthorizationService>(injection_types.IAuthorizationService, AuthorizationService );
console.log("IAuthorizationService added...")
container.registerSingleton<IHttpService>(injection_types.IHttpService, HttpService);
console.log("IHttpService added...")
container.register<IFingerprintService>(injection_types.IFingerprintService,   FingerprintService  );
console.log("IFingerprintService added...")
//container.register<IFileUploadService>(injection_types.IFileUploadService, { useClass: FileUploadService });
container.register<IFileUploadService>(injection_types.IFileUploadService, FileUploadService );
console.log("IFileUploadService added...")
container.register<IDocumentDataService>(injection_types.IDocumentDataService, DocumentDataService)
console.log("IDocumentDataService added...")
container.register<IXmlService>(injection_types.IXmlService, XmlService)
console.log("IXmlService added...")
console.log("All done")
console.log("check resolve test service...")
return container;
}
export const services = inject();

injection_types.ts

const injection_types = 
{
    ILocalStorageService: Symbol.for("ILocalStorageService"),
    ITestSubService : Symbol.for("ITestSubService"),
    IApiPathService : Symbol.for("IApiPathService"),
    IAuthorizationService : Symbol.for("IAuthorizationService"),
    IHttpService : Symbol.for("IHttpService"),
    IFingerprintService : Symbol.for("IFingerprintService"),
    IFileUploadService : Symbol.for("IFileUploadService"),
    IDocumentDataService : Symbol.for("IDocumentDataService"),
    AuthorizationRoutes : Symbol.for("AuthorizationRoutes"),
    FileUploaderRoutes : Symbol.for("FileUploaderRoutes"),
    DocumentDataRoutes : Symbol.for("DocumentDataRoutes"),
    Validator : Symbol.for("Validator"),
    Container : Symbol.for("container"),
    IXmlService : Symbol.for("IXmlService"),
    ServiceResolver : Symbol.for("ServiceResolver"),
};

ITestSubService.ts

interface ITestSubService
{
    GetTest() : string;
}

class TestSubService implements ITestSubService
{
    public  GetTest() : string
    {
       try 
       {
         return "Hi from test service!"
       } 
       catch (error) 
       {
           console.log(error);
           return "ERRROR!"
       }
    }
}

ILocalStorage.ts

interface ILocalStorageService
{
    TestSubService() :void

}
@injectable()
class LocalStorageService implements ILocalStorageService
{
    constructor(@inject(injection_types.ITestSubService) private test : ITestSubService){ }

    TestSubService():void
    {
        console.log(this.test.GetTest())
        console.log(this.test.GetTest())
    }
}

Before update to vue3 (in vue2) all works fine... package.json "reflect-metadata": "^0.1.13", "register-service-worker": "^1.7.1", "tsyringe": "^4.3.0", "vue": "^3.0.2",

MeltingMosaic commented 4 years ago

Hmm, strangely, it looks like it thinks that injection_types is undefined. If you test it just using raw strings rather than symbols at both registration and injection time does it work?

ghost commented 4 years ago

If i am using raw string, all ok, service resolving... enum tokens {testtoken = "test"} not work ITestSubService : Symbol.for("ITestSubService") not work const testToken = { testtoken : "test" work } and rawstring work too

What could be the problem with symbol or enum?

MeltingMosaic commented 3 years ago

Hmm, dumb question, but are you importing injection_types from whatever file it's in? Additionally, if you could post a representative sample that I could run and debug myself, that could be helpful.

ghost commented 3 years ago

.... I just found where i got error If import container after import App, i got error.

import "reflect-metadata";

import { createApp } from 'vue';

import App from './App.vue';
import { services } from './injection/injection.config';
import './registerServiceWorker'

import router from './router'
import "reflect-metadata";

import { createApp } from 'vue';
import { services } from './injection/injection.config';
import App from './App.vue';

import './registerServiceWorker'

import router from './router'

MeltingMosaic thank you very much for your help!