31
votes

Effets NgRX - Le type 'Observable <unknown>' n'est pas assignable au type 'Observable <Action>'

En travaillant avec NgRX 8, mes collègues et moi sommes souvent confrontés à un message d'erreur étrange lors de la mise en œuvre des effets.

Le type «Observable <unknown>» ne peut pas être affecté au type «Observable <Action> | ((... args: any []) => Observable <Action>) '

Il est lié à des problèmes de type. C'est vraiment ennuyeux que le message soit si peu spécifique et qu'il marque l'effet complet. Cela apparaît fréquemment et est vraiment difficile à résoudre.

entrez la description de l'image ici

Nous nous demandons s'il y a quelque chose que nous pouvons faire pour identifier rapidement les problèmes ou si nous sommes capables de déstructurer le message d'une manière ou d'une autre. Je ne cherche pas ici une solution spécifique, mais plutôt une procédure "comment déterminer rapidement ce qui ne va pas".

Merci et bravo!

Collection de possibilités

C'est ce que nous avons fait jusqu'à présent et aussi des idées issues de commentaires et de réponses.

  • Dans la plupart des cas, ce n'est probablement pas un problème avec les actions
  • Cela peut être une mauvaise déstructuration pour le paramètre switchMap
  • L'un des opérateurs RxJS n'est pas importé
  • Des paramètres et une valeur de retour incorrects dans le service peuvent être la raison
  • Des types incohérents dans le service et l'action peuvent également être la raison
  • Si rien ne vous aide et que vous êtes sûr, il n'y a rien de mal: Recharger TypeScript

Nous espérons toujours une astuce pour briser le message d'erreur.


9 commentaires

qu'est-ce que addComment ?


Quel est le type de addCommentFailed / of(addCommentFailed) ?


Il n'est pas important de résoudre ce problème, mais d'obtenir en quelque sorte une méthode générale pour traiter cette erreur. addComment est le retour de la méthode createAction et n'est probablement pas le problème. Les méthodes d' failed et de success sont également des actions createAction createAction. L' failed n'est pas le problème ici, bien que je n'exécute pas l'action ayant échoué en tant que fonction.


Je l'ai recréé localement et je n'obtiens aucune erreur. Avez-vous vérifié si l'espace de travail de votre éditeur utilise la version dactylographiée des projets? Parfois, VSCode commence à utiliser la version TS installée globalement.


Dans le paramètre ofType, vous devez le passer en tant que $ {votre action} .type, si l'action est créée via createAction


@KiraAG non je n'ai pas besoin de faire ça, ça marche avec d'autres effets comme celui-ci.


@ cyr-x c'est ce qui m'a aidé à résoudre un problème antérieur et ce que je fais régulièrement si rien ne résout les erreurs de type. Bon point.


De plus, la commande de Reload typescript project ou le redémarrage d'un éditeur aide parfois, surtout si d'énormes projets sont ouverts pendant une longue période. Mais je suppose que ce n'est pas le problème ici.


Ouais désolé. C'était lors de la phase initiale de la sortie du V8, nous avons dû utiliser le paramètre de type .


9 Réponses :


2
votes

En fait, vous devez traiter les actions créées par createAction comme une fonction et l'appeler pour renvoyer l'objet action. Vérifiez cette desc . Donc, votre of(addCommentFailed) doit être of(addCommentFailed()) .


3 commentaires

Merci pour votre réponse, je sais que je dois exécuter la fonction mais cela ne cause pas l'erreur. Comme je l'ai mentionné dans les commentaires précédents, je ne cherche pas non plus une solution spécifique de la capture d'écran mais une instruction générale "déterminer rapidement ce qui ne va pas".


D'accord. Maintenant je comprends. Mais je crois qu'il ne peut y avoir aucune instruction générale. Je suggérerais d'utiliser les types de retour d'actions créés. Cette suggestion est basée sur cette spécification de test , vous pouvez voir un message d'erreur semblable étant asserted.Also vérifier cette


Je pense que c'est la bonne réponse. votre méthode catchError renvoie la fonction et non la valeur de la fonction. L'ajout de parenthèses à toutes les actions est requis dans NgRX 8.



-1
votes

J'ai également vu une erreur similaire.

Type 'Observable<unknown>' is not assignable to type 'Observable<Action> | ((...args: any[]) => Observable<Action>)'

Le moyen le plus rapide d'identifier ce qui a pu arriver est après | séparateur. Le type est généralement Observable<{something}> ou Observable<{something | another}> . Il y a un | séparateur et le deuxième élément n'est pas un observable. C'est peut-être le problème.

Si, par exemple, l'observable s'attend à avoir Observable<Action> , mais qu'il existe un opérateur de canal, il renvoie autre chose que Observable<Action> . Le type inattendu sera ajouté au type attendu dans le message d'erreur, car pour Observable, T n'existe pas dans ((...args: any[]) => Observable<Action>)

mon interprétation du message est

Le type interne d'un objet est inconnu, car nous ne savons pas à quel type s'attendre Observable<Action> = Action ou ((...args: any[]) => Observable<Action>) = unknown

si le problème n'est pas immédiatement évident, je viens de commenter chaque opérateur de tube un par un et voir si l'erreur va bien. Si c'est le cas, maintenant je sais que cet opérateur est le problème, que je me concentre sur l'opérateur et trouve le problème.

Je suis intéressé de lire les réponses des autres utilisateurs. Ces deux voies m'indiquent toujours dans la bonne direction.


1 commentaires

Salut @ mr.vea, merci pour votre réponse :) Je vais jeter un oeil à ceci aujourd'hui.



5
votes

J'ai eu exactement le même problème et dans mon cas, c'était à cause d'accolades mal placées et d'une importation manquante.

Voici ce que j'ai fait pour le déboguer et le résoudre.

Divisez la fonction canalisable interne en fonctions individuelles. C'est la première étape pour moi en raison de la syntaxe complexe et des accolades auto-complétées de vscode, parfois une accolade existe au mauvais endroit et ce n'est pas facile à trouver. Cela résout également presque tous les autres problèmes (importations manquantes, types de retour incorrects, etc.) car le code à déboguer est beaucoup plus petit et vscode met en évidence cette erreur individuelle de la sous-fonction.

C'est ce que j'avais avant

//login.service.ts

performLogin() : Observable<boolean> {
        throw "Something went wrong";
        //return of(true);
    }

Modifié ceci en dessous

 performLogin$ = createEffect(() => 
       this.actions$.pipe(
           ofType(performLogin),
           mergeMap(() => this.performLogin()),
           catchError((error ) => { console.log("HELLO"); return EMPTY})
       )
    );

    performLogin() {
        return this.loginService.performLogin()
        .pipe(map(() => loginSuccess()));
    }

Un autre avantage que j'ai obtenu de cette approche est que le bloc catchError sur le tube interne ne se déclenche pas (comme dans l'exemple d'effets, cela devrait fonctionner). Il fallait donc l'inclure dans le bloc extérieur canalisable. Ici, l'erreur est détectée et fonctionne comme prévu. Mais toujours comprendre pourquoi cela ne fonctionne pas.

Pour résumer, le service de connexion effectue les opérations suivantes (lancer une erreur ou renvoyer Observable de true)

    performLogin$ = createEffect(() => 
       this.actions$.pipe(
           ofType(performLogin),
           mergeMap(() => this.loginService.performLogin().pipe(
               map(() => loginSuccess())
           )))
    );

J'espère que cela t'aides.


1 commentaires

Bonjour, merci pour votre réponse, je vais tester votre stratégie aujourd'hui et vous le faire savoir :)



53
votes

Version rapide
commenter createEffect(() => ,
corriger les erreurs que votre IDE (VSCode) signale,
ajoutez createEffect(() => retour.

Alternative - la réécriture comme celle-ci fonctionne également

@Effect()
navigateToDashboard$ = this.actions$.pipe(
    ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
    map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
    map((team: Team) => team.TeamID),
    switchMap(id => of(new routerActions.Go({ path: ['/team', id, 'populate'] })))
)

Additionnel

Aucune erreur après avoir fait ce qui précède? La vérification de type fait son travail correctement et vous indique que vous devriez mapper sur une Observable<Action> ou pour un effet purement secondaire en ajoutant le deuxième argument { dispatch: false } (c'est-à-dire ne pas distribuer une action). Voir la documentation sur les effets NgRx


Réponse plus ancienne (l'utilisation de @Effect est inutile et n'est pas obligatoire)

Le moyen le plus simple que j'ai trouvé pour déboguer est d'écrire de manière version 7 avec le décorateur @Effect et une fois terminé, réécrire à l'aide de createEffect .

Donc pour déboguer:

Type 'Go' is not assignable to type 'ObservableInput<any>'

ce qui donne l'erreur non utile d'écrire comme (ajouter un décorateur, supprimer createEffect(() => , supprimer le crochet final),

Cannot find name 'SwitchMap'

Maintenant nous obtenons une erreur

@Effect()
navigateToDashboard$ = this.actions$.pipe(
    ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
    map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
    map((team: Team) => team.TeamID),
    SwitchMap(id => new routerActions.Go({ path: ['/team', id, 'populate'] }))
)

Suivi par

  navigateToDashboard$ = createEffect(() =>
    this.actions$.pipe(
      ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
      map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
      map((team: Team) => team.TeamID),
      SwitchMap(id => new routerActions.Go({ path: ['/team', id, 'populate'] }))
    )
  )

Réparer cela donne

someEffect$ = createEffect(() => {
  return this.actions$.pipe(
    ...
  )
})

Maintenant, réécrivez en termes NgRx 8. Pas joli mais ça marche.


3 commentaires

createEffect(() => fonctionne parfaitement, merci beaucoup!


@ngfelixl pas tout à fait parfaitement, si je mets mon projet dans un mode dactylographié plus strict ( "strict": true dans tsconfig.json ) alors cette approche ne fonctionne pas toujours.


D'accord, mais je veux dire parfaitement comparé au problème d'origine.



0
votes

J'ai eu ce problème à cause d'un opérateur d'importation manquant ici

// events.effects.ts

import { Observable, of } from 'rxjs';

...

Je l'ai corrigé en ajoutant cette ligne de code en haut

// events.effects.ts
...

getEvents$: Observable<Action> = createEffect(
    () => this.actions$.pipe(
      ofType(EventsActions.getEvents),
      switchMap(action =>
        this.eventService.getEvents().pipe(
          map(events => EventsActions.getEventsSuccess({ events })),
          catchError(error => of(EventsActions.getEventsError({ error })))
        )
      )
    )
  );
...


1 commentaires

Je suis d'accord, cette erreur apparaît si l'un des opérateurs ou "constructeurs" observables n'est pas importé. Mais il apparaît également si les types ne correspondent pas et dans plusieurs autres cas. Merci beaucoup pour votre réponse et je pense qu'il y a quelqu'un qui s'intéresse à votre solution. :)



4
votes

En cas de résolution de ce problème et en utilisant l'exemple officiel de ngrx 8.

ofType<ReturnType<typeof myAction>>

Une solution simple et rapide peut être de mettre "n'importe quel" type.

ofType<MySuperActions>

N'oubliez pas les importations pour les opérateurs rxjs, les observables.

En cas de traitement de la charge utile d'action - props, définissez un type d'action.

loadMovies$: any = createEffect((): any => this.actions$.pipe(
    ofType('[Movies Page] Load Movies'),
    mergeMap(() => this.moviesService.getAll()
      .pipe(
        map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
        catchError(() => EMPTY)
      ))
   )
);

ou

loadMovies$ = createEffect(() => this.actions$.pipe(
    ofType('[Movies Page] Load Movies'),
    mergeMap(() => this.moviesService.getAll()
      .pipe(
        map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
        catchError(() => EMPTY)
      ))
   )
);


0 commentaires

0
votes

J'ai rencontré un problème similaire aujourd'hui (même message d'erreur) et c'était parce que TypeScript ne pouvait pas déduire correctement le type de retour de Array#flatMap . Je suppose que ce sera la même chose pour Array#map , RxJS#map ou tout tableau qui n'a pas été explicitement tapé.

Voici le code qui s'est écrasé:

import { Action } from '@ngrx/store';
...
    switchMap((action) => {
        return action.cart.flatMap((cartItem: CartItem) : Action[] => {
...

J'ai donc reçu le même message d'erreur:

Le type «Observable <unknown>» ne peut pas être affecté au type «Observable <Action> | ((... args: any []) => Observable <Action>) '

Pour résoudre ce problème, je devais juste dire à TypeScript que ma fonction de mappage renvoyait un tableau d' Action :

this.actions$.pipe(
    ofType(ActionType),
    switchMap((action) => {
        return action.cart.flatMap((cartItem: CartItem) => {
            if (x) {
                return [
                    ActionCreator1(params1),
                    ActionCreator2(params2)
                ];
            } else {
                return [];
            }
        }
    })
)

De plus, taper avec Action[] est plus sûr qu'avec any !


0 commentaires

0
votes

Dans mon cas, j'ai utilisé un opérateur lodash à l'intérieur de l'un des opérateurs rxjs. Il s'est avéré que je n'avais pas @types/lodash . Après npm i -D @types/lodash , mon problème a été résolu.


0 commentaires

0
votes

J'ai lutté avec ce problème pendant un certain temps pour découvrir à la fin que le code VS importait automatiquement Observable partir d'une bibliothèque au lieu de rxjs

J'espère que cela évitera à quelqu'un de se cogner la tête.


0 commentaires