如何为HTML结构嵌套对象定义TypeScript类型?
为HTML标签嵌套对象定义TypeScript类型
问题背景
你有如下字面量对象:
const objectIdea = { form: { div: { attributes: { cssClasses: ["Container", "flex"], }, h1: { attributes: { text: "Hello world 2!", }, }, }, div: { h1: { text: "And so on." }, }, main: { h1: { attributes: { text: "Hello world!" } }, }, }, };
该对象的键代表HTML标签,值是包含attributes属性和子HTML标签的对象,目标是通过这个对象自动生成HTML组件,对象支持任意长度和嵌套层级,结构需遵循:
HTMLTag: { attributes: HTMLTagAttributes, // 应从父标签推断 HTMLTag: { // 子标签 attributes: HTMLTagAttributes, // 应从父标签推断 HTMLTag: // 以此类推 }
你尝试了如下代码但未解决问题:
interface IComponentObject<T extends keyof HTMLElementTagNameMap = keyof HTMLElementTagNameMap> { T:{ attributes: T } }
解决方案
要实现这个需求,我们需要用递归类型处理嵌套结构,同时结合TypeScript内置的HTMLElementTagNameMap来关联HTML标签和对应的DOM元素类型,进而推断属性。
步骤1:定义标签对应的属性类型
基于标签对应的DOM元素,提取其可用属性,同时允许自定义额外属性(比如示例中的cssClasses、text):
type AttributesForTag<T extends keyof HTMLElementTagNameMap> = Partial<HTMLElementTagNameMap[T]> & Record<string, any>;
这里用Partial让所有DOM属性变为可选,同时通过Record<string, any>支持自定义属性。
步骤2:定义递归的组件节点类型
组件节点需要包含可选的attributes(对应当前标签的属性),以及任意数量的子标签(递归引用自身类型):
type ComponentNode<T extends keyof HTMLElementTagNameMap = keyof HTMLElementTagNameMap> = { attributes?: AttributesForTag<T>; } & { [K in keyof HTMLElementTagNameMap]?: ComponentNode<K>; };
这个类型的逻辑是:每个节点可以有当前标签的属性,同时可以嵌套任意其他HTML标签作为子节点,子节点同样遵循这个结构。
步骤3:定义根对象类型
根对象是多个顶级HTML标签的集合:
type ComponentObject = { [K in keyof HTMLElementTagNameMap]?: ComponentNode<K>; };
验证类型
现在可以用这个类型约束你的字面量对象:
const objectIdea: ComponentObject = { form: { div: { attributes: { cssClasses: ["Container", "flex"], }, h1: { attributes: { text: "Hello world 2!", }, }, }, div: { h1: { attributes: { text: "And so on." } }, }, main: { h1: { attributes: { text: "Hello world!" } }, }, }, };
注意:原示例中第二个div下的h1直接写了text属性,不符合结构要求,需要放到attributes对象中才能通过类型校验。
优化:严格约束自定义属性(可选)
如果希望自定义属性有更严格的类型,比如明确cssClasses是字符串数组、text是字符串,可以修改AttributesForTag:
type CustomAttributes = { cssClasses?: string[]; text?: string; }; type AttributesForTag<T extends keyof HTMLElementTagNameMap> = Partial<HTMLElementTagNameMap[T]> & CustomAttributes;
这样TypeScript会自动校验自定义属性的类型,避免错误。
内容的提问来源于stack exchange,提问作者Gonzalo Ramos
相关产品推荐
相关产品推荐

