如何基于styled-system与styled-components创建可匹配层级的通用Heading组件
可自动匹配层级标签的通用Heading组件实现
原有实现的报错根因
- 动态拼接生成的
h${level}标签被TS推断为宽泛的string类型,没有匹配到React内置的合法HTML元素类型映射 - 原Props定义把
as暴露为必传的可选参数,和“根据level自动匹配标签”的设计目标冲突 - 动态组件没有明确的类型约束,TS无法校验styled-system透传的TextAlign、Color属性是否符合组件接收要求
修正后的实现方案
- 收窄类型范围,把动态标签的类型限定在合法的h1-h6区间
- 调整组件props定义:把
as设为可选的标签覆盖参数,而非强制外部传入的内容 - 给动态组件加上明确的React类型标注,消除TS的类型校验报错
完整可运行代码
import css from "@styled-system/css" import styled from "styled-components" import { color, ColorProps, textAlign, TextAlignProps, variant } from "styled-system" import type { ComponentPropsWithoutRef, ElementType } from "react" // 定义严格的标题层级、对应标签类型 type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6 type HeadingTag = `h${HeadingLevel}` // 组件对外暴露的Props类型 type HeadingProps = { /** 标题层级,对应自动渲染h1-h6标签 */ level: HeadingLevel /** 可选:手动覆盖渲染的HTML标签,满足特殊场景使用需求 */ as?: ElementType } & TextAlignProps & ColorProps & ComponentPropsWithoutRef<HeadingTag> // 基础渲染组件 const HeadingBase = ({ level, as, children, ...props }: HeadingProps) => { // 优先使用传入的as标签,否则根据level自动生成对应h标签 const Component = as ?? (`h${level}` as HeadingTag) // 给动态组件做类型标注,解决TS属性校验报错 const TypedComponent = Component as ElementType return <TypedComponent {...props}>{children}</TypedComponent> } // 保留原有样式配置逻辑 const headingBaseStyle = css({ fontFamily: "heading", fontWeight: "heading", }) const levelVariant = variant({ prop: "level", variants: { 1: { fontSize: 7, lineHeight: 7 }, 2: { fontSize: 6, lineHeight: 6 }, 3: { fontSize: 5, lineHeight: 5 }, 4: { fontSize: 4, lineHeight: 4 }, 5: { fontSize: 3, lineHeight: 3 }, 6: { fontSize: 2, lineHeight: 2 }, }, }) // 生成最终的styled组件 const Heading = styled(HeadingBase)<HeadingProps>( headingBaseStyle, levelVariant, textAlign, color ) export default Heading
实际使用示例
- 基础用法:根据level自动渲染对应标签,匹配对应字号行高
{/* 自动渲染h2标签,应用二级标题样式 */} <Heading level={2} color="primary.600" textAlign="center">页面二级标题</Heading> - 特殊场景覆盖:手动指定as标签,保留原有标题样式
{/* 渲染为div标签,应用三级标题样式 */} <Heading level={3} as="div">自定义容器的三级标题</Heading>
实现注意点:
- 不需要在外部把
level和as做强制绑定,用户只需要传入level就可以自动渲染正确的标题标签,as参数仅作为特殊场景的出口,不会干扰默认逻辑 - 显式的类型断言是为了兼容TS低版本的类型推断缺陷,在TS 5.0+版本中,
h${level}的模板字面量可以自动匹配React的HTML标签类型定义,类型报错会自动消失
内容的提问来源于stack exchange,提问作者lolol
相关产品推荐
相关产品推荐

