bruno
bruno

Reputation: 3

ngrx/effects type inference error

I'm trying to implement an authentication functionality using effects. These are the relevant classes

// auth.actions.ts

import { Action } from '@ngrx/store';
import { AuthVM, ParamsVM } from '../models';
import { Credential } from '../interfaces';

export const FETCH_AUTH_INFO = 'FETCH_AUTH_INFO';
export const AUTHENTICATE_WITHOUT_CREDENTIALS = 'AUTHENTICATE_WITHOUT_CREDENTIALS';

export class FetchAuthInfo implements Action {
  readonly type = FETCH_AUTH_INFO;

  constructor(public payload = undefined) { }
}

export class AuthenticateWithoutCredentials implements Action {
  readonly type = AUTHENTICATE_WITHOUT_CREDENTIALS;

  constructor(public payload: ParamsVM = undefined) { }
}

export type AuthActions =   FetchAuthInfo | AuthenticateWithoutCredentials;

#

// auth.effects.ts

import { Injectable } from "@angular/core";
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { Action } from '@ngrx/store';
import * as fromAuthActions from './auth.actions';
import { Storage } from '@ionic/storage';
import { fromPromise } from "rxjs/observable/fromPromise";
import { AuthVM, ParamsVM } from '../models';
import { of } from 'rxjs/observable/of';
import { AuthInfoFetched, AuthenticateWithoutCredentials} from './auth.actions';


@Injectable()
export class AuthEffects {

  constructor(
    private actions$: Actions, 
    private storage: Storage
  ) { }

  @Effect() fetchAuthInfo$: Observable<Action> = this.actions$
    .ofType(fromAuthActions.FETCH_AUTH_INFO)
    .mergeMap(() => fromPromise(this.storage.get('authInfo')))
    .mergeMap((data: AuthVM) => {
      if (data) {
        return of(new AuthInfoFetched(data));
      } else {
        return of(new AuthenticateWithoutCredentials());
      }
    });

}

I'm getting this error:

The type argument for type parameter 'R' cannot be inferred from the usage. Consider specifying the type
            arguments explicitly. Type argument candidate 'AuthInfoFetched' is not a valid type argument because it is
            not a supertype of candidate 'AuthenticateWithoutCredentials'. Types of property 'type' are incompatible.
            Type '"AUTHENTICATE_WITHOUT_CREDENTIALS"' is not assignable to type '"AUTH_INFO_FETCHED"'.

As far as I know, an Action Observable should be returned at the end of the operators chain. I can't understand why I'm getting the error since AuthInfoFetched and AuthenticateWithoutCredentials both implement the Action interface.

This is the package.json file if it is of any use

{
  "name": "app",
  "version": "0.0.1",
  "author": "Ionic Framework",
  "homepage": "http://ionicframework.com/",
  "private": true,
  "scripts": {
    "clean": "ionic-app-scripts clean",
    "build": "ionic-app-scripts build",
    "lint": "ionic-app-scripts lint",
    "ionic:build": "ionic-app-scripts build",
    "ionic:serve": "ionic-app-scripts serve"
  },
  "dependencies": {
    "@angular/common": "^4.4.6",
    "@angular/compiler": "^4.4.6",
    "@angular/compiler-cli": "^4.4.6",
    "@angular/core": "^4.4.6",
    "@angular/forms": "^4.4.6",
    "@angular/http": "^4.4.6",
    "@angular/platform-browser": "^4.4.6",
    "@angular/platform-browser-dynamic": "^4.4.6",
    "@ionic-native/admob-free": "^4.4.2",
    "@ionic-native/core": "3.12.1",
    "@ionic-native/splash-screen": "3.12.1",
    "@ionic-native/status-bar": "3.12.1",
    "@ionic/app-scripts": "2.1.3",
    "@ionic/storage": "2.0.1",
    "@ngrx/effects": "^4.1.1",
    "@ngrx/store": "^4.1.1",
    "@ngrx/store-devtools": "^4.1.1",
    "body-parser": "^1.18.2",
    "cordova-admob-sdk": "^0.11.1",
    "cordova-android": "^6.4.0",
    "cordova-plugin-admob-free": "^0.11.0",
    "cordova-plugin-console": "^1.1.0",
    "cordova-plugin-device": "^1.1.7",
    "cordova-plugin-splashscreen": "^4.1.0",
    "cordova-plugin-statusbar": "^2.3.0",
    "cordova-plugin-whitelist": "^1.3.3",
    "cordova-promise-polyfill": "0.0.2",
    "cordova-sqlite-storage": "^2.1.2",
    "cors": "^2.8.4",
    "express": "^4.16.2",
    "ionic-angular": "3.6.0",
    "ionic-plugin-keyboard": "^2.2.1",
    "ionicons": "3.0.0",
    "morgan": "^1.9.0",
    "rxjs": "5.4.3",
    "sw-toolbox": "3.6.0",
    "typescript": "2.3.4",
    "zone.js": "0.8.12"
  },
  "devDependencies": {
    "ionic": "3.19.1"
  },
  "description": "An Ionic project",
  "cordova": {
    "plugins": {
      "cordova-sqlite-storage": {},
      "cordova-plugin-console": {},
      "cordova-plugin-device": {},
      "cordova-plugin-splashscreen": {},
      "cordova-plugin-statusbar": {},
      "cordova-plugin-whitelist": {},
      "ionic-plugin-keyboard": {},
      "cordova-plugin-admob-free": {}
    },
    "platforms": [
      "android"
    ]
  }
}

Any help would be much appreciated.

Upvotes: 0

Views: 420

Answers (2)

bruno
bruno

Reputation: 3

I also got it working typecasting the returned values to the "Action" type

@Effect() fetchAuthInfo$: Observable<Action> = this.actions$
.ofType(FETCH_AUTH_INFO)
.mergeMap(() => fromPromise(this.storage.get('authInfo')))
.mergeMap((data: AuthVM) => {
  if (data) {
    return of(<Action>new AuthInfoFetched(data));
  } else {
    return of(<Action>new AuthenticateWithoutCredentials());
  }
});

Upvotes: 0

ramon22
ramon22

Reputation: 3618

In the effect you are returning two different types of action using if/else, so the type can not be inferred for the effect. you can use rxjs if operator or use a temp variable. note ngrx now uses pipes with rxjs 6

Here is code showing different ways

  //one way
  @Effect() fetchAuthInfo$: Observable<Action> = this.actions$
    .ofType(fromAuthActions.FETCH_AUTH_INFO)
    .map(() => fromPromise(this.storage.get('authInfo')))
    .switchMap((data: AuthVM) => {
      let result;
      if (data) {
        result = of(new AuthInfoFetched(data));
      } else {
        result = of(new AuthenticateWithoutCredentials());
      }
      return result;
   });

   //second way 
   @Effect() fetchAuthInfo$: Observable<Action> = this.actions$
    .ofType(fromAuthActions.FETCH_AUTH_INFO)
    .map(() => fromPromise(this.storage.get('authInfo')))
    .switchMap((data: AuthVM) => Observable.if(() => data,
        of(new AuthInfoFetched(data)),
        of(new AuthenticateWithoutCredentials())
        )
    );

    //the new effect way in ngrx
     @Effect() fetchAuthInfo$ = this.actions$.pipe(
               ofType(fromAuthActions.FETCH_AUTH_INFO),
               map(() => fromPromise(this.storage.get('authInfo'))),
               switchMap((data: AuthVM) => iif(() => data,
                          of(new AuthInfoFetched(data)),
                          of(new AuthenticateWithoutCredentials()))
              )
      );

Upvotes: 1

Related Questions