I have a React component written in Typescript called GradientText.tsx:
import React, { ReactNode } from "react"
import { cn } from "@/lib/utils"
// Define a mapping of valid HTML tags and their corresponding JSX components
const typeMap = {
h1: "h1",
h2: "h2",
p: "p",
// Add other valid HTML tags here
}
interface GradientTextProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode // Specify ReactNode type for children prop
className?: string
type?: keyof JSX.IntrinsicElements
}
function GradientText({
children,
className = "",
type = "h1",
}: GradientTextProps) {
// Get the corresponding JSX component based on the 'type' prop
const TagComponent = typeMap[type as keyof typeof typeMap] || "h1"
return (
<TagComponent
className={`leading-tight ${cn(
"bg-gradient-to-r from-primary via-red-400 to-pink-500 text-transparent bg-clip-text",
className,
)}`}
>
{children}
</TagComponent>
)
}
// Set defaultProps for className
GradientText.defaultProps = {
className: "",
type: "h1",
}
export default GradientText
This component is supposed to accept text along with an HTML tag name and color it with a pre-defined gradient. For example:
<GradientText type="p">gradient paragraph</GradientText>
<GradientText type="h1">gradient header</GradientText>
The gradient displays the expected output in the browser. However, in VSCode, it shows two errors:
Type ‘{ children: ReactNode; className: string; }’ is not assignable to type ‘IntrinsicAttributes’.
Property ‘children’ does not exist on type ‘IntrinsicAttributes’.
Again, the text displays alright despite this error.
UPDATE
As suggested in the first answer below, added extends React.HTMLAttributes<HTMLElement>
to my interface, but the problems persist. TS still saying:
Type ‘{ children: ReactNode; className: string; }’ is not assignable to type ‘IntrinsicAttributes’.
Property ‘children’ does not exist on type ‘IntrinsicAttributes’.
UPDATE 2:
One way to prevent the error completely without breaking functionality is to just use type
as string with multiple if
s to render the desired element:
import React, { ReactNode } from "react"
import { cn } from "@/lib/utils"
interface GradientTextProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode // Specify ReactNode type for children prop
className?: string
type?: string
}
function GradientText({
children,
className = "",
type = "p",
}: GradientTextProps) {
const style = `leading-tight ${cn(
"bg-gradient-to-r from-primary via-red-400 to-pink-500 text-transparent bg-clip-text",
className,
)}`
// Get the corresponding JSX component based on the 'type' prop
if (type === "h1") return <h1 className={style}>{children}</h1>
if (type === "h2") return <h2 className={style}>{children}</h2>
if (type === "h3") return <h3 className={style}>{children}</h3>
if (type === "h4") return <h4 className={style}>{children}</h4>
if (type === "h5") return <h5 className={style}>{children}</h5>
if (type === "h6") return <h6 className={style}>{children}</h6>
return <p className={style}>{children}</p>
}
// Set defaultProps for className
GradientText.defaultProps = {
className: "",
type: "p",
}
export default GradientText
But I was hoping for a way to DRY this algorithm. Instead of having to write half a dozen if
s, I wanted to use a single array-like implementation to dynamically generate the invoked tag.
2
Answers
To have an
interface
that allows your custom components to take custom props such astype
as well as html attribute props or react-specific html props such as usingclassName
instead ofclass
, you should ideallyextend
yourinterface
fromThis will stop the warning you are getting such that
children
andclassName
will now be recognized asIntrinsicAttributes
. TypeScript needs to know this somehow.Your problem is not that simple.
To reach your goal, we need to use something called
Polymorphic components
Read more: here
Simple code like that, you have to defined a
React Component
like this:And use this like this: