如何让TypeScript的multipleSelect函数保留querySelector精确类型推导?
实现带类型推导的多选择器元素查询函数
我编写了一个multipleSelect函数,用于根据传入的选择器对象返回对应DOM元素,但目前所有返回元素的类型都统一为Element,无法像原生querySelector那样自动推导具体的元素类型。
原函数代码
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 }
使用示例
const app = document.querySelector('.app')! const elements = multipleSelect(app, { title: 'h3', list: 'ul', canvas: 'canvas', circle: 'circle', currentLi: 'ul>li.current', })
当前返回类型
const elements: { title: Element; list: Element; canvas: Element; circle: Element; currentLi: Element; }
期望的返回类型
希望能保留querySelector的类型推导特性,让返回类型自动匹配选择器对应的具体元素:
const elements: { title: HTMLHeadingElement; list: HTMLUListElement; canvas: HTMLCanvasElement; circle: SVGCircleElement; currentLi: Element; }
解决方案
要实现这个需求,我们可以利用TypeScript内置的元素标签类型映射,并通过模板字面量类型提取选择器中的标签名,从而推导对应的元素类型。修改后的函数如下:
// 合并HTML和SVG的标签-元素类型映射 type ElementTagNameMap = HTMLElementTagNameMap & SVGElementTagNameMap; // 从选择器字符串中提取第一个标签名(支持处理带层级/类名的选择器) type ExtractTagName<S extends string> = S extends `${infer TagName}${string}` ? TagName extends keyof ElementTagNameMap ? TagName : never : never; // 根据选择器字符串推导对应的元素类型 type SelectorToElement<S extends string> = ExtractTagName<S> extends keyof ElementTagNameMap ? ElementTagNameMap[ExtractTagName<S>] : Element; export default function multipleSelect<T extends Record<string, string>>( parent: ParentNode, selectors: T ) { const selected = {} as { [K in keyof T]: SelectorToElement<T[K]> }; 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 as SelectorToElement<T[key]>; } return selected; }
代码说明
ElementTagNameMap:合并TypeScript内置的HTML和SVG标签-元素类型映射表,覆盖绝大多数常用元素的类型对应关系。ExtractTagName:通过模板字面量类型从选择器字符串中提取第一个标签名(比如从"ul>li.current"中提取"ul"),仅保留映射表中存在的标签名。SelectorToElement:根据提取到的有效标签名,从映射表中获取对应的具体元素类型;若选择器无法提取有效标签名,则返回基础Element类型。- 泛型与类型断言:调整泛型定义以支持遍历选择器对象的键值对,通过类型断言确保DOM元素赋值时的类型匹配。
验证结果
使用原示例调用修改后的函数,elements的类型将自动推导为预期的具体元素类型,完全符合需求。
内容的提问来源于stack exchange,提问作者Yukulélélé
相关产品推荐
相关产品推荐

