Reputation: 2233
I've looked through several existing solutions and posts and couldn't really find any solution.
So, I'm using React with Typescript and styled-component.
One part of my project is the Heading
component. Ideally, I imagined to used it like <Heading level={2}>Hello world!</Heading>
wherever I need it.
Here is the simplified Codesandbox Code of it
However, the <S.Heading
throws Errors in my Linter, even though it seems to work visually speaking.
Error
This JSX tag's 'children' prop expects type 'never' which requires multiple children, but only a single child was provided.ts(2745)
No overload matches this call. This JSX tag's 'children' prop expects type 'never' which requires multiple children, but only a single child was provided.ts(2769)
Not sure what's wrong about my code since I think I followed most recommendations..
Upvotes: 0
Views: 904
Reputation: 42218
Typescript template literal types are not as smart as you might think. You might expect h${props.level}
to evaluate to a union type "h1" | "h2" | ...
based on the type of your props.level
variable. Instead it is just string
. So you will need an as
assertion somewhere in your code in order to declare that this string
is a valid key of JSX.IntrinsicElements
.
Getting the union "h1" | "h2" | ...
as a type is tough because Props['level']
is a union of number
literals rather than string
literals. But we don't really need the union because t doesn't really matter which heading type it is. You can use as "h1"
and you'll be fine.
export default function Heading(props: Props) {
return <S.Heading as={`h${props.level}` as "h1"}>{props.children}</S.Heading>;
}
Out of curiosity I wanted to see if I could extract the valid h*
tags from JSX.IntrinsicElements
. I came up with a solution that almost works based on string length, but I forgot about hr
!
type HTag = {
[K in keyof JSX.IntrinsicElements]: K extends `h${string}${infer B}` ? B extends "" ? K : never : never
}[keyof JSX.IntrinsicElements]
evaluates to:
"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "hr"
Upvotes: 1