Reputation: 8683
I am trying to port https://github.com/catalinmiron/react-typical to TypeScript. However, I am facing some issues.
Here's the screenshot with errors in VSCode:
Here's the same code for brevity:
import React from 'react'
import { type, type as loopedType } from '@camwiegert/typical'
import styles from './styles.module.css'
type Props = {
steps: Array<any>
loop: number
className: string
wrapper: React.Component
}
const Typical: React.FC<Props> = ({ steps, loop, className, wrapper = 'p' }) => {
const typicalRef = React.useRef<HTMLElement>(null)
const Component: string = wrapper
const classNames: string[] = [styles.typicalWrapper]
if (className) {
classNames.unshift(className)
}
React.useEffect(() => {
if (loop === Infinity) {
type(typicalRef.current, ...steps, loopedType)
} else if (typeof loop === 'number') {
type(typicalRef.current, ...Array(loop).fill(steps).flat())
} else {
type(typicalRef.current, ...steps)
}
}, [typicalRef])
return <Component ref={typicalRef} className={classNames.join(' ')} />
}
export default React.memo(Typical)
I am unable to write type for Component
.
I tried doing the following too:
const Component = React.Component | string
But it says 'Component' refers to a value, but is being used as a type here. Did you mean 'typeof Component'?
near return <Component .../>
with underline over Component
.
I am also unable to convert the typicalRef
as typicalRef.current
always throws error by showing red squiggly lines under it. Same thing with flat()
as well as classNames.join(' ')
.
I am losing my brain over it. Can't seem to figure it out. Would love any pointers?
Upvotes: 0
Views: 598
Reputation: 8683
I couldn't solve it using directly as I think TypeScript itself doesn't support it https://github.com/microsoft/TypeScript/issues/28892
But I did solve it using React.createElement
syntax. My entire code looks like this right now:
import React from 'react'
import { type, type as loopedType } from '@camwiegert/typical'
import styles from './styles.module.css'
type Props = {
steps: Array<any>
loop: number
className?: string
wrapper: keyof JSX.IntrinsicElements
} & React.HTMLAttributes<HTMLOrSVGElement>
const Typical = ({ steps, loop, className, wrapper: Wrapper = 'p' }: Props) => {
const typicalRef: React.RefObject<HTMLElement> = React.useRef<HTMLElement>(null)
const classNames: string[] = [styles.typicalWrapper]
if (className) {
classNames.unshift(className)
}
const typicalStyles: string = classNames.join(' ')
React.useEffect(() => {
if (loop === Infinity) {
type(typicalRef.current as HTMLElement, ...steps, loopedType)
} else if (typeof loop === 'number') {
type(typicalRef.current as HTMLElement, ...Array(loop).fill(steps).flat())
} else {
type(typicalRef.current as HTMLElement, ...steps)
}
}, [typicalRef])
return React.createElement(Wrapper, {
ref: typicalRef,
className: typicalStyles,
})
}
export default React.memo(Typical)
Upvotes: 2
Reputation: 17474
I think you have to set your wrapper
to React.ComponentType<React.PropsWithRef<any>>
which is accurate React type and rename your wrapper directly (do not re-assign in the body that tsc
can be confused with mixed type as string) so your code might change as following:
type Props = {
steps: Array<any>
loop: number
className: string
wrapper: React.ComponentType<React.PropsWithRef<any>>
}
const Typical: React.FC<Props> = ({ steps, loop, className, wrapper: Component = 'p' }) => {
const typicalRef = React.useRef<HTMLElement>()
const classNames: string[] = [styles.typicalWrapper]
if (className) {
classNames.unshift(className)
}
React.useEffect(() => {
if (loop === Infinity) {
type(typicalRef.current, ...steps, loopedType)
} else if (typeof loop === 'number') {
type(typicalRef.current, ...Array(loop).fill(steps).flat())
} else {
type(typicalRef.current, ...steps)
}
}, [typicalRef])
return <Component ref={typicalRef} className={classNames.join(' ')} />
}
Since flat
method is only available from "es2019" which means you have to include it in the the tsc
build by adding to the tsconfig.json
following:
"compilerOptions": {
"lib": ["ES2019"]
},
Upvotes: 0
Reputation: 1588
For the Component part, set it in the props instead of a new variable:
const Typical: React.FC<Props> = ({ wrapper:Component = 'p' }) => {
return <Component />
}
Upvotes: 0