Angular interception is causing an error due to issues with data types

Currently working on developing an HR Manager app with Angular for the frontend and .NET for the backend. I encountered an issue while trying to set the type in the interceptor for HttpRequests.

auth.interceptor.service.ts

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable, from, tap } from 'rxjs';
import { CoreModule } from './core.module';
import { AuthService } from './auth-service.component';
import { Constants } from '../constants';
import { Router } from '@angular/router';

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
  constructor(
    private _authService: AuthService,
    private _router: Router,  
  ) { }
  
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.url.startsWith(Constants.apiRoot)) {
      return from(this._authService.getAccessToken().then(token => {
        const headers = new HttpHeaders().set('Authorization', `Bearer ${token}`);
        const authReq = req.clone({ headers });
        return next.handle(authReq).pipe(tap(_ => { }, error => {
          var respError = error as HttpErrorResponse;
          if (respError && (respError.status === 401 || respError.status === 403)) {
            this._router.navigate(['/unauthorized']);
          }
        })).toPromise();
      }));
    } else {
      return next.handle(req);
    }
  }
}

The AuthService is responsible for managing login, logout, token retrieval, and other necessary functions.

auth-service.component.ts

import { Injectable } from '@angular/core';
import { CoreModule } from './core.module';
import { UserManager, User } from 'oidc-client';
import { Constants } from '../constants';
import { Subject } from 'rxjs';

@Injectable()
export class AuthService {
    private _userManager: UserManager;
    private _user: User | undefined;
    private _loginChangedSubject = new Subject<boolean>();

    loginChanged = this._loginChangedSubject.asObservable();

    constructor() {
        const stsSettings = {
            authority: 'https://localhost:5443/',
            client_id: 'spa-client',
            redirect_uri: `${Constants.clientRoot}signin-callback`,
            scope: 'openid profile projects-api',
            response: 'id_token token',
            post_logout_redirect_uri: `${Constants.clientRoot}signout-callback`,
        };

        this._userManager = new UserManager(stsSettings);
    }

    // Remaining code for AuthService...

    // Error message and error code details would be placed here

Upon further investigation, the error seems to be originating from the following code snippet:

return from(this._authService.getAccessToken().then(token => {
        const headers = new HttpHeaders().set('Authorization', `Bearer ${token}`);
        const authReq = req.clone({ headers });
        return next.handle(authReq).pipe(tap(_ => { }, error => {
          var respError = error as HttpErrorResponse;
          if (respError && (respError.status === 401 || respError.status === 403)) {
            this._router.navigate(['/unauthorized']);
          }
        })).toPromise();
      }));

Answer №1

With the release of RxJS 7, there has been a significant change in the return type of the Observable's toPromise() method. The updated return type now accurately reflects the possibility of Observables yielding zero values. This alteration could potentially cause issues for some projects, as the return type has transitioned from Promise<T> to Promise<T | undefined>. More information on this deprecation can be found here.

Instead of using toPromise(), consider implementing the following approach:

intercept(req: HttpRequest<any>, next: HttpHandler) {
    if (!req.url.startsWith(Constants.apiRoot)) {
      return next.handle(req);
    }
    return from(this._authService.getAccessToken()).pipe(
      switchMap((token) => next.handle(req.clone({ setHeaders: { Authorization: 'Bearer ' + token } })),
      catchError((error) => {
        if (error instanceof HttpErrorResponse && (error.status === 401 || error.status === 403)) {
          this._router.navigate(['/unauthorized']);
          return EMPTY; // Uncomment this line if you wish to throw an error
        }
        return throwError(error);
      })
    );
  }

Answer №2

'Observable<HttpEvent<any> | undefined>' not being assignable to type 'Observable<HttpEvent<any>>'
indicates that you are trying to assign a nullable value to a non-nullable type. This issue may arise due to the strictNullChecks flag being set to true in your tsconfig.ts.

To resolve this, consider implementing a type guard.

Assuming that yourVar has the type

Observable<HttpEvent<any> | undefined>
, the code snippet below demonstrates proper variable assignment:

let observable: Observable<HttpEvent<any>> = null;

if (yourVar) {
  observable = yourVar;
} else {
  // additional handling logic required here
}

For returning values, adhere to the following pattern:

fn(): Observable<HttpEvent<any>> = {
  if (yourVar) {
    return yourVar;
  }
  throw new Error(); // or implement custom logic that returns an observable
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Component loader with dynamic rendering for multiple components

Currently, I am in search of a method to dynamically render components within my Angular application. While exploring various options, I came across the concept of dynamic component loading in Angular (refer to https://angular.io/guide/dynamic-component-lo ...

ERROR: The use of @xenova/transformers for importing requires ESM

When working with my Node.js application, I usually start by importing the necessary modules like this: import { AutoModel, AutoTokenizer } from '@xenova/transformers'; Afterwards, I utilize them in my code as shown below: const tokenizer = awai ...

What is the best way to access Passport.js user information on the client side?

Currently, my application utilizes Angular2 on the front-end, which is served by a separate backend server running express. This back-end server also uses passport.js for Google Oauth authentication. Once a user is authenticated through Passport (using Go ...

The hosting feature is not visible when using the command ng add @angular/fire

As I embark on the journey of building an Angular system, I encounter a hiccup. Despite setting up Firebase in the console and verifying the account with firebase-tools, the option for @angular/fire hosting is missing when I try to add it using ng add @ang ...

Accessing Angular's Observable Objects

I am currently in the process of learning Angular and trying to work with Observables. However, I am facing an issue where I cannot extract data from an Observable when it is in object form. public rowData$!: Observable<any[]>; UpdateGrid() { this ...

The Angular @HostListener beforeunload Event is a powerful way to handle

I've implemented the following code snippet in my main app.component.ts file within my Angular 17 project: @HostListener("window:beforeunload", ["$event"]) onTabClose($event: BeforeUnloadEvent) { $event.preventDefault(); ...

Sending data from Spark to my Angular8 projectLearn how to seamlessly transfer data from your

I am utilizing Spark 2.4.4 and Scala to retrieve data from my MySQL database, and now I am looking to showcase this data in my Angular8 project. Can anyone provide guidance on the process? I have been unable to locate any relevant documentation so far. ...

The search is on for the elusive object that Angular 2/4

Why am I encountering the error message saying "Cannot find name 'object'"? The error message is: Error: core.service.ts (19,23): Cannot find name 'object'. This error occurs in the following line of code: userChange: Subject<ob ...

Refresh Material-Ui's Selection Options

Is there a way to properly re-render the <option> </option> inside a Material UI select component? My goal is to transfer data from one object array to another using the Material UI select feature. {transferData.map(data => ( <option ...

Enhancing editor.selections in Visual Studio Code Extension

I'm currently developing a vscode extension that involves moving each selection of a multi-selection to the right by one character. The challenge lies in updating the TextEditor.selections array, which stores all current selections in the GUI. When I ...

How to retrieve a variable from an object within an array using AngularJS code

I recently started learning TypeScript and AngularJS, and I've created a new class like the following: [*.ts] export class Test{ test: string; constructor(foo: string){ this.test = foo; } } Now, I want to create multiple in ...

Bringing Typescript functions into the current module's scope

Is it possible to import and reference a module (a collection of functions) in typescript without the need for the Module. prefix? For instance: import * as Operations from './Operations'; Can I access Operations.example() simply as example()? ...

Image not being sent to the back-end server

I've successfully set up the back-end to retrieve files using the post method in Postman. However, I'm facing an issue with the Angular2 front-end as the service code seems to be incorrect. data1 contains the image data. addData(postData: Imag ...

Top Tip for Preventing Angular Version Compatibility Issues

Here is an illustration that delves into my inquiry ----> Version Conflict The dilemma arises when my product has a dependency on a node package, which in turn relies on a specific version of Angular, denoted as version #y. However, my product itself ...

Hold off until all commitments are fulfilled in Firestore

Is there a way to ensure that all promises are resolved before moving on to the next line of code? Currently, it seems like it's moving to the next line without completing the operation below. I want to make sure that the forEach loop is fully execute ...

RXJS - Trigger a function based on a specific condition being fulfilled by a value emitted from an observable

I have created a search field with autocomplete functionality. By using an observable that monitors changes in the text field, I am able to trigger actions based on user input. this.term.valueChanges .debounceTime(300) .distinctUntilChange ...

What is the recommended approach for returning two different types in a TypeScript function?

My API function currently performs a post request and returns an Observable of ModelAResponse, which is an interface I have defined. I now want to modify this function so that it can return an Observable of either ModelAResponse or ModelBResponse based on ...

Can one create a set of rest arguments in TypeScript?

Looking for some guidance on working with rest parameters in a constructor. Specifically, I have a scenario where the rest parameter should consist of keys from an object, and I want to ensure that when calling the constructor, only unique keys are passed. ...

Generating custom error messages with specified format when utilizing routing-controllers

I'm currently working on a Node APP that utilizes the routing-controllers library. In the README file, there is a section titled Throw HTTP errors, which includes a self-explanatory example. However, I encountered an issue when attempting to replicat ...

Fix for sorting issue in Angular 4.4.x mat-table header

I am facing an issue with my mat-table sorting header. I have followed the examples and decorated the columns accordingly: <ng-container matColumnDef="id"> <mat-header-cell *matHeaderCellDef mat-sort-header> Id </mat-header-cell> & ...