Unhandled JS Exception: Invalid character: '@' (Using Typescript, Expo, React Native & InversifyJS)
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

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);
.....
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",
May retated to babel: https://github.com/inversify/InversifyJS/issues/1050
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
+1 Encountered the same issue in a React app (non-native) with typescript. Unfortunately @MehediEhteshum's solution is not working for me
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.
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
I was just running into this issue on a new mono repo project with Expo. Thank you so much!