Object is possibly null inside a callback

Viewed 630

I've got the following code:

             let ctx = ref.current.getContext("2d");
             if(ctx){
                ctx.lineWidth=1; // here is ok!
                ctx.strokeStyle=props.barStroke??"darkgray";// here is ok!
                ctx.fillStyle=props.barFill??"blue"; // here is ok!
                props.data.map((v,i)=>{
                    ctx.fillRect(i*10,0,10,props.height); //here it complain about ctx is possibly null!!
                    ctx.strokeRect(1*10,0,10,props.height);
                })

             }

I've checked ctx and so ctx.lineWidth works without errors, but inside the callback, it signals that the object could be possibly null. If I use ctx?.method code works anyway, so how can be ctx, checked for null outside the callback, became null inside it? Here a link showing the problem.

3 Answers

As per this:

This is working as intended. The type checker knows that ctx is non-null at the time the callback function is created, but it doesn't know that ctx is non-null when the callback is called (because bar is a mutable property that could be changed before or between calls to the callback function).

The suggested way to do this is to copy ctx into a const local and then use that in the callback.

Try this :

      let ctx = ref.current.getContext("2d");
         if(ctx) {
            ctx.lineWidth=1; // here is ok!
            ctx.strokeStyle=props.barStroke??"darkgray";// here is ok!
            ctx.fillStyle=props.barFill??"blue"; // here is ok!
            props.data.map((v,i)=>{
                ctx.fillRect(i*10,0,10, v.height); //here it complain about ctx is possibly null!!
                ctx.strokeRect(1*10,0,10, v.height);
            })

         }

Looks like props is't predicted not ctx. Because ctx is refered like local variable already.

See Matthieu Riegler's answer for a description of why this is happening. Another solution is to use a for loop instead of a callback, since the callback (which TS isn't smart enough to determine is occurring exactly when it's being declared in the .map callback) is what's causing the issue:

let ctx = document.querySelector<HTMLCanvasElement>('foo')!.getContext("2d");
if (ctx) {
    const props = {
        data: ['x', 'y'],
        height: 10
    }
    for (const [i, v] of props.data.entries()) {
        ctx.fillRect(i * 10, 0, 10, props.height);
        ctx.strokeRect(1 * 10, 0, 10, props.height);
    }
}
Related