angulardependency-injectionrxjsbehaviorsubject

Why is a BehaviorSubject with initialized value of 1 is not assignable to type 'number'?


I have a service that uses a behavior subject that is initialized with a value of 1. That value can be toggled between 1 and 2 to represent which player's turn it is.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class PlayerTurnService {
  private playerTurnState$ = new BehaviorSubject<number>(1);

  nextPlayerTurn() {
    this.playerTurnState$.next(this.playerTurnState$.value === 1 ? 2 : 1);
  }

  getCurrentPlayerTurn() {
    return this.playerTurnState$.asObservable();
  }
}

I'm using property binding to assign that value to a component that is just a label stating which players turn it is.

<app-player-label [currentPlayerTurn]="playerTurn$ | async"></app-player-label>


@Component({
  selector: 'app-player-label',
  template: `
    <p>Player {{playerTurn}} turn </p>
  `,
  standalone: true,
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlayerTurnLabelComponent {
  
  @Input() playerTurn!: number;
}

But I'm getting the following error.

Type 'number | null' is not assignable to type 'number'.

I can fix this error but setting the input as being either a number or null.

@Input() playerTurn!: number | null;

But this doesn't feel right. Especially because I'm using the non null assertion operator to let typescript know that this value will never be null.

Is there something I'm missing here about how a Behavior Subject initializes this value? Why is this happening?

I've recreated the problem in a Stackblitz for demo purposes.


Solution

  • Since async pipe returns returns null or undefined value by default.

    You can work around this by using provide default value like this on template.

    <app-player-label [playerTurn]="(playerTurn$ | async) ?? 0"></app-player-label>
    <p>{{playerTurn$ | async}}</p>
    <button (click)="toggle()">Toggle turn</button>
    

    Example