Angular 19集成Firebase启动报错:TypeError: cls is not a constructor
TypeError: cls is not a constructor错误 依赖版本
"@angular/cdk": "19.0.4", "@angular/common": "19.0.5", "@angular/compiler": "19.0.5", "@angular/core": "19.0.5", "@angular/fire": "19.0.0",
相关代码文件
main.ts
import { ApplicationConfig, enableProdMode, importProvidersFrom } from '@angular/core'; import { environment } from './environments/environment'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; import { AppRoutingModule } from './app/app-routing.module'; import { AppComponent } from './app/app.component'; import { provideHttpClient, withInterceptors, withInterceptorsFromDi } from '@angular/common/http'; import { authInterceptor } from './app/interceptors/auth.interceptor'; import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; import { getAuth, provideAuth } from '@angular/fire/auth'; if (environment.production) { enableProdMode(); } export const appConfig: ApplicationConfig = { providers: [ importProvidersFrom(BrowserModule, AppRoutingModule), provideAnimations(), provideHttpClient(withInterceptorsFromDi(), withInterceptors([authInterceptor])), provideFirebaseApp(() => initializeApp(environment.firebaseConfig)), provideAuth(() => getAuth()) ] }; bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
app.component.ts(用户变更订阅逻辑)
private subscribeUserEvents() { this.authService.user$.subscribe(async (user) => { if (user) { this.authService.currentUserSig.set({ id: user.uid, username: user.displayName, email: user.email, token: await user.getIdToken() }); } else { this.authService.currentUserSig.set(null); } }); }
auth.service.ts
import { Injectable, signal } from '@angular/core'; import { Auth, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, setPersistence, browserLocalPersistence, user } from '@angular/fire/auth'; import { Router } from '@angular/router'; export interface User { id: string; username: string; email: string; token: string; } @Injectable({ providedIn: 'root' }) export class AuthService { constructor(private auth: Auth, private router: Router) { setPersistence(this.auth, browserLocalPersistence).then(() => console.log('Persistence set to browser local')); } user$ = user(this.auth); currentUserSig = signal<User | null | undefined>(undefined); register(email: string, password: string) { return createUserWithEmailAndPassword(this.auth, email, password); } async login(email: string, password: string) { return signInWithEmailAndPassword(this.auth, email, password); } async logout() { await signOut(this.auth); await this.router.navigate(['/sign-in']); } }
auth.guard.ts
import { Injectable, inject } from '@angular/core'; import { CanActivate, Router, UrlTree } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; @Injectable({ providedIn: 'root', }) export class AuthGuard implements CanActivate { private authService = inject(AuthService); private router = inject(Router); canActivate(): Observable<boolean | UrlTree> { return this.authService.user$.pipe( take(1), map(user => { if (user) { return true; } else { return this.router.createUrlTree(['/sign-in']); } }) ); } }
auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; import { AuthService } from '../services/auth.service'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const authService = inject(AuthService); const token = authService.currentUserSig()?.token; if (token) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next(req); };
错误信息
ERROR TypeError: cls is not a constructor
at _getInstance (index-dfc2d82f.js:1954:16)
at index-dfc2d82f.js:2839:59
at Generator.next ()
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:17:1)
at asyncToGenerator.js:22:1
at new ZoneAwarePromise (zone.js:2702:25)
at asyncToGenerator.js:14:1
at _ZoneDelegate.invoke (zone.js:369:28)
at ZoneImpl.run (zone.js:111:43)
问题描述
应用功能正常,Token可传递至后端,路由守卫工作正常,但首次打开应用时会抛出上述错误,怀疑与main.ts中的Firebase提供者配置有关。
解决方案
该错误通常由Firebase Auth初始化时机与Angular依赖注入生命周期冲突导致,核心原因是setPersistence在Auth实例未完全初始化时调用引发的问题,可通过以下步骤修复:
调整AuthService中setPersistence的执行时机
不要在构造函数中直接调用setPersistence,改为异步初始化方法,确保Auth实例完全就绪:@Injectable({ providedIn: 'root' }) export class AuthService { constructor(private auth: Auth, private router: Router) {} user$ = user(this.auth); currentUserSig = signal<User | null | undefined>(undefined); async initPersistence() { await setPersistence(this.auth, browserLocalPersistence); console.log('Persistence set to browser local'); } // 其他方法保持不变 }在AppComponent的
ngOnInit中触发初始化:constructor(private authService: AuthService) {} ngOnInit() { this.authService.initPersistence(); this.subscribeUserEvents(); }优化AppComponent中的用户订阅逻辑
避免在subscribe回调中使用async/await导致的时序问题,改用switchMap处理异步操作:private subscribeUserEvents() { this.authService.user$.pipe( switchMap(async (user) => { if (user) { const token = await user.getIdToken(); return { id: user.uid, username: user.displayName, email: user.email, token }; } return null; }) ).subscribe(userData => { this.authService.currentUserSig.set(userData); }); }确认Firebase提供者顺序
确保provideFirebaseApp在provideAuth之前(当前配置已符合,但需保持该顺序),保证Firebase App先完成初始化再创建Auth实例。
这些修改可解决首次加载时的初始化冲突,消除cls is not a constructor错误。
内容的提问来源于stack exchange,提问作者Weeedooo

