InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

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

Open developernm opened this issue 6 years ago • 4 comments

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$": "<rootDir>/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": [ "<rootDir>" ] }, "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<IInformService>(TYPES.InformService).to(InformService); IoCContainer.bind<interfaces.Newable<IInformHttpClient>>(TYPES.InformHttpClient).toConstructor<InformHttpClient>(InformHttpClient); // IoCContainer.bind<IInformHttpClient>(TYPES.InformHttpClient).to(InformHttpClient);

export default IoCContainer;

types.ts ==>

export const TYPES = { InformHttpClient: Symbol.for('interfaces.Newable<InformHttpClient>'), 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);

.....

developernm avatar Jul 25 '19 10:07 developernm

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",

ph-teven avatar Sep 16 '19 15:09 ph-teven

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

csr632 avatar Dec 01 '19 13:12 csr632

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

MehediEhteshum avatar Jul 02 '24 12:07 MehediEhteshum

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

bitfist avatar Aug 12 '24 03:08 bitfist

I'm afraid this is not an inversify issue. @inject is a legacy decorator and, as such, it doesn't exist as a js decorator. Typescript transpiled code don't emit a decorator but a call to the inject function. Transpilers are expected to transpile legacy decorators the same way.

notaphplover avatar Oct 22 '24 10:10 notaphplover

i get the same error in react native. Here's how I fixed the error.

add the following library : yarn add -D babel-plugin-transform-typescript-metadata

then in the babel.config.js folder add this :

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
     ...
    'babel-plugin-transform-typescript-metadata',
     ...
  ],
};

Hope this helps you

reyhanadp avatar Feb 04 '25 09:02 reyhanadp

I was just running into this issue on a new mono repo project with Expo. Thank you so much!

JohnValanidas avatar Feb 21 '25 02:02 JohnValanidas