You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 19集成Firebase启动报错:TypeError: cls is not a constructor

首次加载Angular Firebase应用时出现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实例未完全初始化时调用引发的问题,可通过以下步骤修复:

  1. 调整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();
    }
    
  2. 优化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);
      });
    }
    
  3. 确认Firebase提供者顺序
    确保provideFirebaseApp在provideAuth之前(当前配置已符合,但需保持该顺序),保证Firebase App先完成初始化再创建Auth实例。

这些修改可解决首次加载时的初始化冲突,消除cls is not a constructor错误。

内容的提问来源于stack exchange,提问作者Weeedooo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 10:27:02