Try this, importing ReactChildren from react.
I wish i could explain this better, but i know typescript is expecting a JSX/TSX element, so we could use ReactElement for the return type.
For a better explanation on the children type
https://stackoverflow.com/a/58123882/7174241
import React, { ReactChildren, ReactElement, ReactNode } from "react";
interface RequireType {
children: ReactChildren | ReactNode | ReactElement;
required_permission: string;
}
const RequirePermission = ({ children, required_permission }: RequireType):ReactElement => {
const user = useContext(AuthContext);
let hasPermission = false;
if (user.state == AuthState.Authorized) {
user.user?.realm_access?.roles.forEach((role) => {
if (permissions[role].includes(required_permission)) {
hasPermission = true;
}
});
}
return <>{hasPermission ? children : null}</>
};
export default RequirePermission;
Answer from BARNOWL on Stack OverflowHi,
code example
I am starting with the TS in React and I have no idea how to rewrite my wrapper component to TS.
I have multiple inputs components (MyInput and MyTextArea in the example) and each have a custom props. I want to create a wrapper which can take the input as a prop but also can reuse types from the input. e.g.:
this is how it looks like without the wrapper
<MyInput
customProp1="regular input"
value={value}
onChange={e => setValue(e.target.value)}
/>and this is how it should looks like with the wrapper
<MyWrapper
input={MyInput}
customProp1="wrapped input"
value={value2}
onChange={e => setValue2(e.target.value)}
/>Currently in my example the MyWrapper has the "any" type. How to replace the "any" with a "MyInput"/"MyTextArea" ts types? In this example MyWrapper need the access to "value" and "onChange" props so I can not pass the input as a child.
reactjs - How to best type a TypeScript collection of React wrappers - Stack Overflow
Can't specify correct types for a wrapper component around `Button`, in TypeScript
reactjs - Typescript + React: Typing a functional wrapper component that extends a given class component - Stack Overflow
how to create a wrapper that changes the props for the component in react
Videos
You can do it with a combination of types and a utility function that will enforce the props for the given component:
type ComponentAndProps<C extends React.ElementType<any>> = [C, React.ComponentPropsWithRef<C>];
type WrapperSet = ComponentAndProps< React.ElementType<any> >[];
function makeGroup<C extends React.ElementType<any>>(Component: C, props: React.ComponentPropsWithRef<C>): ComponentAndProps<C> {
return [Component, props];
}
Here's a usage example:
export const Container: React.FC = () => {
// these could be any arbitrary wrapper components
const wrappers: WrapperSet = [
makeGroup(WrapperA, { label: "WrapperA" }),
makeGroup(WrapperB, { title: "foo" }),
makeGroup(WrapperB, {}), // Error: missing 'title' property
];
const content = <span>Original content</span>;
return wrap(content, wrappers);
};
The trick is the makeGroup() helper function that allows TypeScript to infer and enforce the props type for the component.
If you just use the tuple notation then the props end up as any and TypeScript can't enforce the props:
const wrappers2: WrapperSet = [
[WrapperA, { label: "WrapperA" }],
[WrapperB, {}] // BAD - no error, TypeScript can't infer the props type for the component here
];
Final note - React has a number of utility types for extracting the props type from a component.
I chose ComponentPropsWithRef just in case you have a component that uses refs, but adjust as necessary:
React.ComponentProps<Component>
React.ComponentPropsWithRef<Component>
React.ComponentPropsWithoutRef<Component>
You can create type util which will accept two components and produce a tuple of wrappers and corresponding props:
import React, { FC, ComponentProps } from "react";
type Wrap<Comps extends FC<any>[]> = {
[Comp in keyof Comps]: Comps[Comp] extends React.JSXElementConstructor<any>
? [Comps[Comp], ComponentProps<Comps[Comp]>]
: never
}
type WrapperSet = Wrap<[typeof WrapperA, typeof WrapperB]>
export const Container: React.FC = () => {
// these could be any arbitrary wrapper components
const wrappers: WrapperSet = [
[WrapperA, { label: "WrapperA" }],
[WrapperB, { title: "foo" }],
];
const content = <span>Original content</span>;
return wrap(content, wrappers);
}; // compiles
But there is still a problem with:
function wrap(children: JSX.Element, wrappers: WrapperSet): JSX.Element {
let content = children;
wrappers.forEach((wrapper) => {
const [ComponentType, props] = wrapper;
content = <ComponentType {...props}>{content}</ComponentType>;
});
return content;
}
Likewise, multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.
This is why ComponentType expects an intersection of all props and not a union. TS is unable to figure out which prop correspond to each component in dynamic loop.
In order to fix it, we need to create extra function:
const iteration = <Comp extends React.JSXElementConstructor<any>>(
Comp: Comp,
props: ComponentProps<Comp>,
content: JSX.Element
) => <Comp {...props} >{content}</Comp>
And the whole code:
import React, { FC, ComponentProps } from "react";
type Wrap<Comps extends FC<any>[]> = {
[Comp in keyof Comps]: Comps[Comp] extends React.JSXElementConstructor<any>
? [Comps[Comp], ComponentProps<Comps[Comp]>]
: never
}
type WrapperSet = Wrap<[typeof WrapperA, typeof WrapperB]>
export const Container: React.FC = () => {
// these could be any arbitrary wrapper components
const wrappers: WrapperSet = [
[WrapperA, { label: "WrapperA" }],
[WrapperB, { title: "foo" }],
];
const content = <span>Original content</span>;
return wrap(content, wrappers);
};
const WrapperA: React.FC<{ label: string }> = ({ label, children }) => (
<>
<div>WrapperA: {label}</div>
{children}
</>
);
const WrapperB: React.FC<{ title: string }> = ({ title, children }) => (
<>
<div>WrapperB: {title}</div>
{children}
</>
);
const iteration = <Comp extends React.JSXElementConstructor<any>>(
Comp: Comp,
props: ComponentProps<Comp>,
content: JSX.Element
) => <Comp {...props} >{content}</Comp>
function wrap(children: JSX.Element, wrappers: WrapperSet) {
let content = children;
wrappers.forEach((wrapper) => {
const [ComponentType, props] = wrapper;
content = iteration(ComponentType, props, content)
});
return content
}
Playground
I believe You are asking about React.ButtonHTMLAttributes<HTMLButtonElement>.
import React from 'react'
import styled from 'styled-components'
const Button = styled.button<ButtonProps>`
background-color: ${(props) => props.color};
`;
type ButtonProps = {
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
const WrappedButton: React.FunctionComponent<ButtonProps> = ({ children, ...rest }) => (
<div>
<Button {...rest}>{children}</Button>
</div>
);
If you want to use raw html <button>, you mignt be interested in: JSX.IntrinsicElements["button"];
Check this answer
UPDATE The quick and dirty solution would be :
type ButtonProps = {
} & React.ButtonHTMLAttributes<HTMLButtonElement> & { as?: string | React.ComponentType<any> };
But it is not generic.
There is StyledComponentPropsWithAs type in SC typings, but it is not exported (
If you're trying to suppress native attributes you could do something like this:
interface Props {
type?: 'error' | 'success'
}
type OmitNativeAttrs = Omit<React.HTMLAttributes<HTMLButtonElement>, keyof Props>
export type ButtonProps = Props & OmitNativeAttrs