Home > Mobile >  typescript map QuerySelector on an object of selectors
typescript map QuerySelector on an object of selectors

Time:10-27

I created a function returning a selection for each selector in an object

export default function multipleSelect<T extends string>(
  parent: ParentNode,
  selectors: Record<T, string>
) {
  const selected = {} as {
    [key in keyof typeof selectors]: Element
  }
  for (const [key, selector] of Object.entries(selectors) as [T, string][]) {
    const element = parent.querySelector(selector)
    if (!element) throw new Error(`parent doesn't contain '${key}' element`)
    selected[key] = element
  }
  return selected
}

usage:

const app = document.querySelector('.app')!
const elements = multipleSelect(app, {
  title: 'h3',
  list: 'ul',
  canvas: 'canvas',
  circle: 'circle',
  currentLi: 'ul>li.current',
})

Actually, the elements type is:

const element: {
  title: Element;
  list: Element;
  canvas: Element;
  circle: Element;
  currentLi: Element;
}

I would like to keep querySelector typing such that in my example the elements type is:

const element: {
    title: HTMLHeadingElement;
    list: HTMLUListElement;
    canvas: HTMLCanvasElement;
    circle: SVGCircleElement;
    currentLi: Element;
}

CodePudding user response:

Change the generic constraint to Record<string, string> so you can also get the values with the keys.

export default function multipleSelect<T extends Record<string, string>>(
    parent: ParentNode,
    selectors: { [K in keyof T]: T[K] }, // make TS narrow to string literal types
): {
    [K in keyof T]: T[K] extends keyof HTMLElementTagNameMap // if HTML
        ? HTMLElementTagNameMap[T[K]]
        : T[K] extends keyof SVGElementTagNameMap // if SVG
        ? SVGElementTagNameMap[T[K]]
        : Element; // default to Element
} {
    const selected = {} as Record<keyof T, Element>;
   
    for (const [key, selector] of Object.entries(selectors) as [keyof T, string][]) {
        const element = parent.querySelector(selector);
        if (!element) throw new Error(`parent doesn't contain '${String(key)}' element`);
        selected[key] = element;
    }

    return selected as ReturnType<typeof multipleSelect<T>>;
}

Playground

  • Related