Creating HR Manager app using Angular for frontend and .NET for backend. I get error from assigning type in 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);
}
}
}
AuthService is service for handling login, logout, geting tokens and other needed function.
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);
}
login(): any {
return this._userManager.signinRedirect();
}
isLoggedIn(): Promise<boolean> {
return this._userManager.getUser().then(user => {
const userCurrent = !!user && !user.expired;
if (this._user !== user) {
this._loginChangedSubject.next(userCurrent);
}
this._user = user!;
return userCurrent;
});
}
completeLogin() {
return this._userManager.signinRedirectCallback().then(user => {
this._user = user;
this._loginChangedSubject.next(!!user && !user.expired);
return user;
});
}
logout() {
this._userManager.signoutRedirect();
}
completeLogout() {
this._user = null!;
return this._userManager.signoutRedirectCallback();
}
getAccessToken() {
return this._userManager.getUser().then(user => {
if (!!user && !user.expired) {
return user.access_token;
} else {
return null;
}
});
}
}
I'm receiving this error:
Type 'Observable<HttpEvent<any> | undefined>' is not assignable to type 'Observable<HttpEvent<any>>'.
Type 'HttpEvent<any> | undefined' is not assignable to type 'HttpEvent<any>'.
Type 'undefined' is not assignable to type 'HttpEvent<any>'.ts(2322)
The error is caused here:
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();
}));
CodePudding user response:
'Observable<HttpEvent<any> | undefined>' is not assignable to type 'Observable<HttpEvent<any>>'
means that you are assigning a nullable value to a non nullable type. This could be caused by the flag strictNullChecks
set to true
in your tsconfig.ts
.
The solution is about using a type guard.
Supposing that yourVar
has the Observable<HttpEvent<any> | undefined>
type, if you are assigning a variable you should use:
let observable: Observable<HttpEvent<any>> = null;
if (yourVar) {
observable = yourVar;
} else {
// this requires a different handling!
}
If you are returning a value you should use:
fn(): Observable<HttpEvent<any>> = {
if (yourVar) {
return yourVar;
}
throw new Error(); // or a different handling that returns an observable
}
CodePudding user response:
In RxJS 7, the return type of the Observable's toPromise() method has been fixed to better reflect the fact that Observables can yield zero values. This may be a breaking change to some projects as the return type was changed from Promise<T>
to Promise<T | undefined>
.
https://rxjs.dev/deprecations/to-promise
try this , do not use toPromise()
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; // if you wanna throw error comment out this line
}
return throwError(error);
})
);
}