'ReactNode, ElementType, JSX.Element and "Type 'Element' is not assignable to type 'false | ElementType<any>'", what gives?
I designed a pretty large UI library just to learn and also see how things move. As it grew and I had moved to TS, I realized that I could do so much more: initialize state on my own, provide a "default look" for things while also allowing for people to come in with their component and all that internal, heavy logic would be handled by me. However, I'm having trouble understanding how to correctly type my components. I understand what JSX.Element
, but am having trouble with the others. Check this hook out:
import { useDefaultState } from 'Hooks/useDefaultState';
import type { Dispatch, SetStateAction, ElementType } from 'react';
//The default Component
import { Toggle } from './Toggle';
export type Args = {
id: string;
height?: number;
width?: number;
isToggled?: undefined | boolean;
setIsToggled?: undefined | Dispatch<SetStateAction<boolean>>;
withComponent?: boolean;
as?: false | ElementType;
};
export const useToggleFactory = ({
id,
height = 28,
width = 48,
isToggled = undefined,
setIsToggled = undefined,
withComponent = false,
as = false,
}: Args): {
isToggled: boolean;
setIsToggled: Dispatch<SetStateAction<boolean>>;
content: false | ElementType;
} => {
//Neat hook that initializes state if not provided by the outside world (or uses it if provided)
const [actualIsToggled, actualSetIsToggled] = useDefaultState([isToggled, setIsToggled], false);
let content: false | ElementType = false;
if (withComponent) {
if (as) {
const Component = as;
content = (
<Component
id={id}
height={height}
width={width}
isToggled={actualIsToggled}
setIsToggled={actualSetIsToggled}
/>
);
} else {
content = (
<Toggle id={id} height={height} width={width} isToggled={actualIsToggled} setIsToggled={actualSetIsToggled} />
);
}
}
return {
isToggled: actualIsToggled,
setIsToggled: actualSetIsToggled,
content,
};
};
The whole idea is, if withComponent
is true, then the user also wants something to render back. It then checks to see if there's an as
provided, if there is, it will set content
to whatever that custom component is (and also pass parameters), alternatively, if as
is false
, it will use the Toggle
default component.
Ideally, I would use this hook as such: const { content: MyToggle } = useToggleFactory({ id: 'someId', withComponent: true, as: SomeCustomToggle })
, where as
(namely SomeCustomToggle
) is a reference to a component. Naturally, I could just initialize it with <SomeCustomToggle />
and that'd be no issue, it's just an object, after all, there's no cost to it. Right, so, then I'd just render <MyToggle />
and be done with it. Well, not so fast. TS screams and me and says: Type 'Element' is not assignable to type 'false | ElementType<any>'
, alright, content = (<Component...
would make content
an ElementType<any>
. I don't understand what TS is telling me.
And so, I am not sure exactly what's wrong here. I don't want content
to be JSX.Element
inside of useToggleFactory
, I want it to be something that is useful and can be simply rendered with the <NameHere />
notation.
I would greatly appreciate any help.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|