Search code examples
typescriptvue.jschart.jsoptional-chaining

Type check for getElementById only works with optional chaining, not with type check


When trying to add a gradient to a line chart, I needed to get it's canvas. Before proceeding with the canvas I added a typecheck, however Vetur remarked that the "Object is possibly 'null'.Vetur(2531)"

  mounted() {
    const canv = document.getElementById("line-chart") as HTMLCanvasElement;
    if (canv !== null && canv !== undefined) {
      const gradient = canv
        .getContext("2d")
        .createLinearGradient(0, 0, 0, canv.height);

After researching I attempted to use the optional chaining operator to type check. This approach works, as no error is reported.

  mounted() {
    const canv = document.getElementById("line-chart") as HTMLCanvasElement;
    const gradient = canv
      ?.getContext("2d")
      ?.createLinearGradient(0, 0, 0, canv.height);

I'm confused as to why the first approach doesn't work, since canv is a const, thus can't change. A type check should be enough.

Why is only the optional chaining working in this case?


Solution

  • The problem isn't with canv. The compiler knows that canv itself is not null, but if you look at the return type for canv.getContext("2d"), that is possibly null:

    HTMLCanvasElement.getContext(
      contextId: "2d", 
      options?: CanvasRenderingContext2DSettings | undefined
    ): CanvasRenderingContext2D | null
    

    That is what the "Object is possibly null" error is complaining about. You can fix that with optional chaining after that call, such as:

    if (canv !== null && canv !== undefined) {
        const gradient = canv.getContext("2d")?.createLinearGradient(0, 0, 0, canv.height)
    }
    

    Or you can do a more spelled-out type check:

    if (canv !== null && canv !== undefined) {
        const context = canv.getContext("2d");
        if (context) {
            const gradient = context.createLinearGradient(0, 0, 0, canv.height)
        }
    }
    

    Or anything else that convinces the compiler that canv.getContext("2d") will not be null:

    if (canv !== null && canv !== undefined) {
        const gradient = (canv.getContext("2d") || { createLinearGradient: () => undefined })
            .createLinearGradient(0, 0, 0, canv.height);
    }
    

    Playground link to code