angulartypescriptrxjs

Why Does TypeScript Infer Observable<IdentityToken | null> as Observable<IdentityToken>


I'm working on an Angular service where I'm using BehaviorSubject to manage an IdentityToken. The BehaviorSubject is instiantiated as BehaviorSubject<IdentityToken | null>. Then I derive a new observable from the BehaviorSubject by calling .asObservable(). However, when I hover the derived observable then Typescript infers it as BehaviorSubject<IdentityToken>. It seems to omit the null. From my understanding the derived observable should be the same as the BehaviorSubject.

The relevant code:

  private identityToken = new BehaviorSubject<IdentityToken | null>(
    null
  );
  public identityToken$ = this.identityToken.asObservable(); // hovering the `identityToken$` typescript infers is as `BehaviorSubject<IdentityToken>`

The full code:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { StorageKeys } from '../_models/localStorageKeys';
import { map } from 'rxjs/operators';

interface IdentityToken {
  Role: string;
  Permissions: string[];
}

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private identityToken = new BehaviorSubject<IdentityToken | null>(
    null
  );
  public identityToken$ = this.identityToken.asObservable(); // hovering the `identityToken$` typescript infers is as `BehaviorSubject<IdentityToken>`
    
  public isAuthenticated$ = this.identityToken$.pipe(
    map((token) => !!token)
  );

  constructor() {
    this.identityToken = new BehaviorSubject<IdentityToken | null>(
      this.getStoredToken()
    );
  }

  private getStoredToken(): IdentityToken | null {
    const stored = localStorage.getItem(StorageKeys.IDENTITY_TOKEN);
    if (!stored) return null;

    try {
      return JSON.parse(stored);
    } catch (e) {
      localStorage.removeItem(StorageKeys.IDENTITY_TOKEN);
      return null;
    }
  }
}

Solution

  • This is due to strictNullChecks being false (or unset) in your tsconfig.