Home > Software design >  typescript querySelector wrapper
typescript querySelector wrapper

Time:10-26

I have created in typescript this wrapper for document.querySelector():

function qs(selector: string) {
  const elm = document.querySelector(selector)
  if (!elm) throw new Error(`'${sel}' is not in dom`)
  return elm
}

const canvas = qs('canvas')

canvas.getContext('2d')

Of course I get error TS2339: Property 'getContext' does not exist on type 'Element' on last line

Is there a way to make qr() return type depends of the selector argument like querySelector() does?

I tried this:

function qs<T extends Parameters<typeof document.querySelector>[0]>(
  selector: T
): ReturnType<typeof document.querySelector<typeof selector>> {
  return document.querySelector(selector)
}

But I get these errors:

error TS2344 Type 'T' does not satisfy the constraint 'Element'.
error TS2344 Type 'T' does not satisfy the constraint 'HTMLElementTagNameMap'.
error TS2344 Type 'T' does not satisfy the constraint 'SVGElementTagNameMap'.

In others words, I want the variable canvas to be of type HTMLCanvasElement without to have to do:

const canvas = qs('canvas') as HTMLCanvasElement

CodePudding user response:

It's pretty hard (if not impossible to do feasibly?) to change the return type of querySelector so that it doesn't return null because it has overloads... so here's my workaround:

type Qs = {
    <K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K];
    <K extends keyof SVGElementTagNameMap>(selectors: K): SVGElementTagNameMap[K];
    <E extends Element = Element>(selectors: string): E;
};

const qs: Qs = (selector: string) => {
  const elm = document.querySelector(selector)
  if (!elm) throw new Error(`'${selector}' is not in dom`);
  return elm
}

which is literally copying the type of querySelector but removing null from the return type.

Playground

  • Related