angulartypescriptrxjssubject

Why am I obtaining this error trying to perform Angular rxjs Subject next() method with an undefined value?


I am working on an Angular project following a Udemy course (so the code it should be correct) but I am finding the following problem.

The original code in the course is:

export class TrainingService {

    exerciseChanged: Subject<Exercise> = new Subject<Exercise>();
    
    private availableExercise: Exercise[] = [
        { id: 'crunches', name: 'Crunches', duration: 30, calories: 8 },
        { id: 'touch-toes', name: 'Touch Toes', duration: 180, calories: 15 },
        { id: 'side-lunges', name: 'Side Lunges', duration: 120, calories: 18 },
        { id: 'burpees', name: 'Burpees', duration: 60, calories: 8 }
    ];

    private runningExercise: Exercise;

    getAvailableExercises(): Exercise[] {
        return this.availableExercise.slice();
    }

    startExercise(selectedId: string) {
        this.runningExercise  = this.availableExercise.find(ex => ex.id === selectedId);
        this.exerciseChanged.next({ ...this.runningExercise});

    }
}

So as you can see the course instructor defined this variable:

private runningExercise: Exercise;

The problem is that doing in this way I obtain the following error on the first line of my startExercise() method:

this.runningExercise  = this.availableExercise.find(ex => ex.id === selectedId);

the error is:

Type 'Exercise | undefined' is not assignable to type 'Exercise'.
  Type 'undefined' is not assignable to type 'Exercise'.ts(2322)

Basically it seems that for the compiler the result of the find() method can be undefined so it can't do the assignment.

So I fixed in this way:

private runningExercise: Exercise | undefined;

So now the runningExercise variable can take an Exercise object but also an undefined value (maybe not so elegant). And now I have another problem when I try to execute the next() method on my Subject:

this.exerciseChanged.next({ ...this.runningExercise});

the problem is that it can't be an undefined value:

Error: src/app/training/training.service.ts:24:35 - error TS2345: Argument of type '{ id?: string | undefined; name?: string | undefined; duration?: number | undefined; calories?: number | undefined; date?: Date | undefined; state?: "completed" | "cancelled" | null | undefined; }' is not assignable to parameter of type 'Exercise'.
  Types of property 'id' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.

24         this.exerciseChanged.next({ ...this.runningExercise});
                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~

Why the Udemy code works in the instructor example but it is giving me this error on my PC? Maybe something related Angular version or some sort of code validation?

What is wrong? What am I missing? How can I try to fix it?

Thank you


Solution

  • It's probably works for the Udemy instructor because they are either using an older version of Angular that does not have strict mode enable or they just simply disable it. To fix this problem you can perform a little bit of undefined checking before before emitting the value:

    startExercise(selectedId: string) {
        this.runningExercise  = this.availableExercise.find(ex => ex.id === selectedId);
        if (!runningExcercise) return;
        this.exerciseChanged.next({...this.runningExercise});
    
    }
    

    But yeah you should carefully consider what you want to do when selectedId doesn't match any of your exercises, some options that I could think of is:

    1. Simple ignore it and end the function without emitting exceriseChanged (which is the code example above)
    2. Throw an error because this shouldn't happen
    3. Emit an undefined value for exceriseChanged
    4. Emit an Error for exceriseChanged