Angular13解决Observable<ILoginResponse|undefined>赋值错误
Angular 13 登录接口对接TS类型报错修复
场景说明
在Angular 13项目中对接Web API登录接口,接口返回JSON结构如下:
{ "data": { "id": "3333-3841-43d1-9bae-dddd", "token": "anythingsdsdsdsds", "user": { "id": "33333-3841-43d1-9bae-ea32e1165021", "firstName": "Janet", "lastName": "Wilow", "email": "janet@email.com", "userName": "JWilow" }, "roles": ["Teacher"], "expires": "2022-06-23T15:01:21.4727432+01:00", "refreshToken": "9333333-e67e-4deb-84a6-6efdraw" }, "successful": true, "message": "Login Successfully", "statusCode": 200 }
项目中已提前定义对应TypeScript类型:
// 登录请求参数类型 export interface ILoginRequest { userName: string; password: string; } // 登录响应相关类型 export interface IUser { id: string; firstName?: string; lastName?: string; email: string; userName: string; isAdmin: boolean; isPasswordChanged: boolean; mobileNumber: string; lastLogin: Date; fullname: string; } export interface IData { id: string; token: string; user: IUser; roles: string[]; expires: Date; refreshToken: string; } export interface ILoginResponse { data: IData; successful: boolean; message: string; statusCode: number; }
编写auth.service.ts时,声明login方法返回类型为Observable<ILoginResponse>,初始代码如下:
import { Injectable } from '@angular/core'; import { environment } from 'src/environments/environment'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { BehaviorSubject, ReplaySubject, of } from 'rxjs'; import { Observable } from 'rxjs'; import { ILoginRequest } from 'src/app/features/auth/models/login-request'; import { ILoginResponse } from 'src/app/features/auth/models/login-response'; import { map, tap } from 'rxjs/operators'; import { Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; @Injectable({ providedIn: 'root' }) export class AuthService { baseUrl = environment.baseHost; private currentUserSource = new ReplaySubject<ILoginResponse | null>(1); currentUser$ = this.currentUserSource.asObservable(); token = localStorage.getItem('token'); constructor( private http: HttpClient, private router: Router, private toastr: ToastrService ) { } login(model: ILoginRequest): Observable<ILoginResponse> { return this.http.post<ILoginResponse>(`${this.baseUrl} +'login`, model).pipe( map((data:ILoginResponse)=>{ if(data.successful == true){ return data; } else{ console.log(data.successful); } }) ); } }
编写完成后TypeScript抛出类型错误:
Type 'Observable<ILoginResponse | undefined>' is not assignable to type 'Observable<ILoginResponse>'. Type 'ILoginResponse | undefined' is not assignable to type 'ILoginResponse'. Type 'undefined' is not assignable to type 'ILoginResponse'.ts(2322)
错误定位在http.post的pipe map回调段,要求login方法返回完整的ILoginResponse结构数据。
错误原因
- 核心原因:
map操作符的回调函数存在无返回值的分支。当data.successful为false时,代码仅执行console.log,未返回任何值,该分支默认返回undefined,导致TS推断map的返回值类型为ILoginResponse | undefined,和方法声明的返回类型不匹配。 - 附加问题:接口URL拼接存在语法错误,
`${this.baseUrl} +'login`写法错误,会导致请求地址异常。
修复方案
- 修正接口URL拼接逻辑,正确拼接登录接口路径
- 补全map回调的所有分支返回逻辑:登录成功时返回响应数据,同时完成token存储、用户状态推送的常规鉴权逻辑;登录失败时抛出错误,避免返回
undefined,方便上层组件捕获错误做交互提示。 - 从rxjs导入
throwError操作符处理错误场景,也可配合toastr做统一错误提示。
修复后的完整login方法代码:
// 补充导入throwError import { throwError } from 'rxjs'; // ... 其他代码保持不变 login(model: ILoginRequest): Observable<ILoginResponse> { // 修正URL拼接错误 return this.http.post<ILoginResponse>(`${this.baseUrl}/login`, model).pipe( map((response: ILoginResponse) => { if (response.successful) { // 登录成功后存储token、用户信息,推送当前登录用户状态 localStorage.setItem('token', response.data.token); localStorage.setItem('refreshToken', response.data.refreshToken); localStorage.setItem('user', JSON.stringify(response.data)); this.currentUserSource.next(response); return response; } // 登录失败抛出业务错误,杜绝返回undefined return throwError(() => new Error(response.message || '登录失败,请检查账号密码')); }), tap({ // 统一错误提示 error: (err) => { this.toastr.error(err.message); } }) ); }
注意:如果业务要求不抛出错误,必须保证失败分支也返回完全符合ILoginResponse结构的对象,但抛出错误是更符合RxJS流处理逻辑的写法,方便调用方通过subscribe的error回调处理失败场景。
内容的提问来源于stack exchange,提问作者Ayobamilaye
相关产品推荐
相关产品推荐

