#Class constructor: null property

23 messages · Page 1 of 1 (latest)

high vector
#
class SnakeGame {
    private canvas: HTMLCanvasElement // Property 'canvas' has no initializer and is not definitely assigned in the constructor.ts(2564)

    constructor(canvasId: string) {
        let canvas = document.getElementById(canvasId)
        if (canvas instanceof HTMLCanvasElement) {
            this.canvas = canvas
        }
        else {
            throw new Error('Canvas is not an HTMLCanvasElement')
        }
    }
}

Would you type canvas with | null in this case ? I feel like it's not optimal because it will force to check canvas !== null in every function that use it...

summer spoke
#

since you throw if the canvas was not assigned, i'd use a canvas!: here

#

it basically tells ts "trust me, it's there"

high vector
#

Oh I didn't know this was a thing, thanks!

high vector
#

!resolved

dusk marsh
#

@high vector You shouldn't actually need ! there. It seems TS is smart enough to know that it's always assigned.

inner caveBOT
#
Retsam19#2505

Preview:```ts
class SnakeGame {
private canvas: HTMLCanvasElement // Property 'canvas' has no initializer and is not definitely assigned in the constructor.ts(2564)

constructor(canvasId: string) {
let canvas = document.getElementById(canvasId)
if (canvas instanceof HTMLCa
...```

dusk marsh
#

Is there something else going on in your real example that makes the situation more complicated?

#

It could maybe be a TS version issue, but it works in as many versions back as the TS playground can test, so unless it's a particularly ancient version.

high vector
#

You are right, it works without the ! in the current version.

high vector
# dusk marsh Is there something else going on in your real example that makes the situation m...

Not really, but I have a similar issue with another property 😅 :

class SnakeGame {
    private canvas: HTMLCanvasElement
    private ctx: CanvasRenderingContext2D

    constructor(canvasId: string) {
        let canvas = document.getElementById(canvasId)
        if (canvas instanceof HTMLCanvasElement && canvas.getContext) {
            this.canvas = canvas
            this.ctx = canvas.getContext('2d') // Type 'CanvasRenderingContext2D | null' is not assignable to type 'CanvasRenderingContext2D'.
        }
        else {
            throw new Error('Canvas not found or not supported')
        }
    }
}

I use this.ctx = canvas.getContext('2d')! as a workaround for now

dusk marsh
#

Yeah, the check there isn't correct.

high vector
dusk marsh
#

Checking && canvas.getContext isn't the same thing as checking that canvas.getContext('2d') actually returns something.

high vector
#

You are correct, I will try to tweak the condition

#

Is it common to do this kind of thing in ts? ie. don't use | null but throw if the property can't be set?

dusk marsh
#

It can be. Here's one way to do this:

class SnakeGame {
    private canvas: HTMLCanvasElement
    private context: CanvasRenderingContext2D;

    constructor(canvasId: string) {
        let canvas = document.getElementById(canvasId)
        if (!(canvas instanceof HTMLCanvasElement)) {
            throw new Error('Canvas is not an HTMLCanvasElement')
        }
        this.canvas = canvas
        const ctx = canvas.getContext("2d")
        if(!ctx) throw new Error("Expected to have a 2d canvas context");
        this.context = ctx;
    }
}
high vector
#

Thanks 👌

summer spoke
#

(sorry for the above answer with !, didn't think to check)

dusk marsh
#

Truth be told, though, I also think this sort of thing is fine too:

class SnakeGame {
    private canvas: HTMLCanvasElement
    private context: CanvasRenderingContext2D;

    constructor(canvasId: string) {
        this.canvas = document.getElementById(canvasId) as HTMLCanvasElement;
        this.context = canvas.getContext("2d")!;
    }
}
high vector
#

I try to not use as ... but this will throw an error anyway right (if canvas is not an HTMLCanvasElement or if getContext doesn't exists on canvas)?

dusk marsh
#

It's "unsafe", yeah, but ultimately we're just 'haggling over error message' here.

#

The first version errors with "Canvas is not an HTMLCanvasElement", the second with "cannot read property getContext of undefined". The first error message is better, but the odds of running into this error is fairly low.