inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.15k stars 712 forks source link

Unhandled JS Exception: Invalid character: '@' (Using Typescript, Expo, React Native & InversifyJS) #1113

Open developernm opened 5 years ago

developernm commented 5 years ago

Expected Behavior

@injectable() export class InformService implements IInformService {

private _informHttpClient: InformHttpClient;

public constructor(@inject(TYPES.InformHttpClient) informHttpClient: interfaces.Newable<InformHttpClient>) {
    this._informHttpClient = new informHttpClient(environment.informConfigurationEndPoint);
}

I expect at runtime to be able to successfully run by using the @Inject but fails to do so.

Current Behavior

image

Please provide an example of how to get InversifyJS working with typescript version 3.5.2, React Native version 0.59.8 and InversifyJS version 5.0.1.

Context

This is preventing me from injecting services into the app and calling them at runtime.

Package.json ==> { "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", "eject": "expo eject", "test": "jest", "lint": "tslint -p tsconfig.json src/*/.{ts,tsx}" }, "jest": { "preset": "jest-expo", "verbose": true, "transform": { "^.+\.ts?$": "ts-jest", "^.+\.tsx?$": "ts-jest", "^.+\.js$": "/node_modules/react-native/jest/preprocessor.js" }, "testRegex": "(/tests/.*|(\.|/)(test|spec))\.(jsx?|tsx?)?$", "moduleFileExtensions": [ "ts", "tsx", "js", "jsx", "json", "node" ], "transformIgnorePatterns": [ "node_modules/(?!(react-native|my-project|react-native-button)/)" ], "globals": { "ts-jest": { "babelConfig": false, "tsConfig": "./tsconfig.jest.json" } }, "modulePaths": [ "" ] }, "dependencies": { "@types/react-redux": "^7.1.0", "expo": "^33.0.0", "expo-linear-gradient": "^5.0.1", "inversify": "^5.0.1", "inversify-inject-decorators": "^3.1.0", "moment": "^2.24.0", "prettier": "^1.18.2", "prop-types": "^15.7.2", "react": "^16.8.3", "react-azure-adb2c": "^0.2.0", "react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz", "react-native-azure-ad": "^0.2.4", "react-native-azure-ad-2": "^1.0.5", "react-native-calendar-strip": "^1.3.8", "react-native-dynamic-vector-icons": "^0.0.4", "react-native-elements": "^1.1.0", "react-native-gesture-handler": "^1.3.0", "react-native-iphone-x-helper": "^1.2.1", "react-native-maps": "^0.25.0", "react-native-modalbox": "^1.7.1", "react-native-responsive-screen": "^1.2.2", "react-native-slider": "^0.11.0", "react-native-textinput-effects": "^0.5.1", "react-native-vector-icons": "^6.5.0", "react-native-web": "^0.11.4", "react-navigation": "^3.11.0", "react-navigation-transitions": "^1.0.10", "react-redux": "^7.1.0", "react-test-renderer": "^16.8.6", "redux": "^4.0.1", "reflect-metadata": "^0.1.13", "rn-bottom-drawer": "^1.4.3", "rxjs": "^6.5.2", "rxjs-compat": "^6.5.2" }, "devDependencies": { "@types/jest": "^24.0.15", "@types/react": "^16.8.19", "@types/react-native": "^0.57.60", "@types/react-test-renderer": "^16.8.3", "babel-preset-expo": "^5.1.1", "jest": "^24.8.0", "jest-expo": "^33.0.2", "ts-jest": "^24.0.2", "tslint": "^5.17.0", "tslint-config-airbnb": "^5.11.1", "tslint-react": "^4.0.0", "typescript": "^3.5.2" }, "private": true }

Contructor in service ==> @injectable() export class InformService implements IInformService {

private _informHttpClient: InformHttpClient;

public constructor(@inject(TYPES.InformHttpClient) informHttpClient: interfaces.Newable<InformHttpClient>) {
    this._informHttpClient = new informHttpClient(environment.informConfigurationEndPoint);
}

Inversify.Config.ts ==>

import 'reflect-metadata'; import { Container, interfaces } from "inversify"; import { IInformHttpClient, InformHttpClient } from '../shared/services/inform-service/inform.http-client'; import { TYPES } from './types'; import { InformService } from '../shared/services/inform-service/inform.service'; import { IInformService } from '../shared/interfaces/inform.iservice';

const IoCContainer = new Container(); IoCContainer.bind(TYPES.InformService).to(InformService); IoCContainer.bind<interfaces.Newable>(TYPES.InformHttpClient).toConstructor(InformHttpClient); // IoCContainer.bind(TYPES.InformHttpClient).to(InformHttpClient);

export default IoCContainer;

types.ts ==>

export const TYPES = { InformHttpClient: Symbol.for('interfaces.Newable'), InformService: Symbol.for('InformService') };

Call on component which fails when uncommented but succeeds to run when commented out ==>

import { InformService } from '../shared/services/inform-service/inform.service'; import { RouteDto } from '../shared/services/inform-service/inform.http-client'; import IoCContainer from '../ioc/inversify.config';

export class RouteListComponent extends Component {

private informService = IoCContainer.get<InformService>(TYPES.InformService);

.....

ph-teven commented 4 years ago

I am having the same problem. However when i use the typescript shorthand constructor notation it works for me.

Works:

constructor(
        @inject(DI.VersionCode) private version: number,
        @inject(DI.VersionTag) private versionTag: string,
    ) {
        ....
    }

Invalid character: '@'

constructor(
        @inject(DI.VersionCode) version: number,
        @inject(DI.VersionTag) versionTag: string,
    ) {
        ...
    }

"inversify": "^5.0.1", "typescript": "3.4.4" "react-native": "0.59.10",

csr632 commented 4 years ago

May retated to babel: https://github.com/inversify/InversifyJS/issues/1050

MehediEhteshum commented 2 months ago

In my project, I had the same issue caused by babel and inversify @inject directive usage. I used Metro server (not Expo) though, but I believe the solution must be similar. I couldn't find a standalone solution. After much trial and error, I solved it. I hope my solution will save someone a lot of time.

You can find my detailed answer here >> https://stackoverflow.com/a/78696852/13607767

bitfist commented 1 month ago

+1 Encountered the same issue in a React app (non-native) with typescript. Unfortunately @MehediEhteshum's solution is not working for me