在打字稿中使用 props.children 和 styled-component “as” prop

时间:2021-04-18 22:03:31

标签: reactjs typescript styled-components

我浏览了几个现有的解决方案和帖子,但找不到任何解决方案。

所以,我将 React 与 Typescript 和 styled-component 结合使用。

我的项目的一部分是 Heading 组件。理想情况下,我想像 <Heading level={2}>Hello world!</Heading> 一样在我需要的地方使用它。

Here is the simplified Codesandbox Code of it

然而,<S.Heading 在我的 Linter 中抛出错误,即使它看起来在视觉上工作。

错误

<块引用>

这个 JSX 标签的 'children' 属性需要类型 'never',这需要多个孩子,但只提供了一个孩子。ts(2745)

没有与此调用匹配的过载。 这个 JSX 标签的 'children' 属性需要类型 'never',这需要多个孩子,但只提供了一个孩子。ts(2769)

不确定我的代码有什么问题,因为我认为我遵循了大多数建议..

1 个答案:

答案 0 :(得分:1)

Typescript template literal types 并不像你想象的那么聪明。您可能希望 h${props.level} 根据 "h1" | "h2" | ... 变量的类型计算为联合类型 props.level。相反,它只是 string。因此,您需要在代码中的某处使用 as 断言来声明此 stringJSX.IntrinsicElements 的有效键。

获取联合 "h1" | "h2" | ... 作为类型很困难,因为 Props['level']number 文字的联合,而不是 string 文字。但我们并不真的需要联合,因为 t 并不重要,它是哪种标题类型。您可以使用 as "h1",您会没事的。

export default function Heading(props: Props) {
  return <S.Heading as={`h${props.level}` as "h1"}>{props.children}</S.Heading>;
}

出于好奇,我想看看是否可以从 h* 中提取有效的 JSX.IntrinsicElements 标签。我想出了一个几乎基于字符串长度的解决方案,但我忘记了hr

type HTag = {
    [K in keyof JSX.IntrinsicElements]: K extends `h${string}${infer B}` ? B extends "" ? K : never : never
}[keyof JSX.IntrinsicElements]

评估为:

"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "hr"