I have a React component that accepts as props an inputRef
My Interface
interface Props extends WithStyles<typeof styles> {
body: text
inputRef?: any // I am not sure which TypeScript type I should use
}
My component
const MyComponent = ({
classes,
body,
inputRef
}: Props) => {
<TextField
body={body}
inputRef={inputRef}
/>
}
I am not sure which TypeScript type should I use for inputRef. I currently declare it as any but I want the right type.
I tried to use React.createRef<TextInput>() but I get the error: Namespace 'React' has no exported member 'createRef'
I have as types version: "#types/react": "^16.9.21"
Here is how I figure that out. When I create a reference to an element:
const wrapperRef = useRef<HTMLDivElement>(null);
I hover over the variable wrapperRef in VSCode to see what type it is. In this case I see:
const wrapperRef: RefObject<HTMLDivElement>
So, what you want to pass is RefObject<HTMLDivElement>.
Now if you do want an ElementRef, go ahead and use that (I'm not sure what kind of ref you are trying to pass). When you look at documentation or the actual type file you will see <T>. This is a Typescript Generic. It allows you to pass a type to the type declaration. Here is some documentation on it with many examples.
Related
How can I type a prop that accepts ComponentType or a string?
Suppose the following code.
interface MyComponentProps {
Component: React.ComponentType
}
const MyComponent: React.FC<PropsWithChildren<MyComponentProps>> = ({Component}) => {
return <Component>{children}</Component>
}
<MyComponent
Component="span"
/>
Trying this in TS gives the following error.
Type 'string' is not assignable to type 'ComponentType<{}> | undefined'.
How can I dynamically render a native html tag using JSX?
In terms of react and javascript, your code is correct. The issue here is the types. The string "span" is not a ComponentType. The type you want for Component is React.ElementType. If you check out the definition of ElementType it includes all of the intrinsic jsx elements or a component type.
You can come to this conclusion by yourself by rendering a <span></span> and hovering over or clicking through to the span keyword. You'll see the type is JSX.IntrinsicElements.span. If you click through you can see that IntrinsicElements includes all the html elements. I did a search to see where that type was being used which led me to the React.ElementType
Updated code:
interface MyComponentProps {
Component: React.ElementType
}
const MyComponent: React.FC<PropsWithChildren<MyComponentProps>> = ({Component}) => {
return <Component>{children}</Component>
}
<MyComponent
Component="span"
/>
Side note: React.FC might not be the best approach anymore. Instead it seems to be preferable to just assign a type to the props directly:
const MyComponent = ({Component}: PropsWithChildren<MyComponentProps>) => {
return <Component>{children}</Component>
}
More on that here if you're interested: https://github.com/facebook/create-react-app/pull/8177
I am writing react application with typescript.
To provide a typed props I am using the code below.
type ScheduleBoxContentProps = {
desc: ReactNode,
lottie: LottieProps,
} & Partial<{className: string}>;
As you can see, I want className prop to be optional, but don't want to define defaultProps for it. In addition, desc and lottie props should be provided.
Is there any better way to define optional without default ?
Edit
I am sorry for the missing context.
If I use React.FC with my custom prop type then there is no problem. Because React.FC uses Partial inside of it.
type MyProps = {
className?: string;
}
// no erros and warnings
const Component: React.FC<MyProps> = (props) => <div />;
But I don't want my component to accept the children props. I want my component to alert when children props are coming. Thus I am using function components with code below.
// error: default value for className is not provided.
const Component = ({className}: MyProps) => <div className={className} />;
and it tells me that className's default value are defined. I should define it explicitly. Using code below.
Component.defaultProps = {className: ''};
IMO, it seems little bit unnecessary code, so I decided to use Partial on optional props.
type MyProps = Partial<{className: string}>;
Is there any better way to achieve this? Or, using defaultProps is the best practice?
You can just add ? after the property name:
type ScheduleBoxContentProps = {
desc: ReactNode,
lottie: LottieProps,
// This ? is literally what Partial does
className?: string,
};
As for your edited example, what I think would be the most simple is to set the default value in the destructuring syntax:
const Component = ({className = ''}: MyProps) => <div className={className} />;
What about
className?: string;
This is more of a Typescript and React question but for full disclosure, I'll mention that I ended up here while trying to create a custom header using react-navigation.
I have a simple ImageButton (functional) component, which accepts the following props:
export interface ImageButtonProps extends TouchableOpacityProps {
transparent?: boolean;
}
My custom header looks (roughly) like this:
export default function Header(props: StackHeaderProps) {
const options = props.scene.descriptor.options;
const HeaderRight = options.headerRight as (() => ReactElement<ImageButtonProps>);
return (<HeaderRight transparent={true}/>); // <-- Typescript complains here
}
It's safe to assume that options.headerRight will always return an ImageButton, which in fact renders just fine. The only issue is that the property transparent is always undefined and also Typescript throws the following error:
TS2322: Type '{ transparent: boolean; }' is not assignable to type 'IntrinsicAttributes'. Property 'transparent' does not exist on type 'IntrinsicAttributes'.
So my question is, how can I properly cast a ReactElement to my custom component, ImageButton and its props?
Your error is because the type you casted it to doesn't take any arguments or props.
This means, a function which takes no arguments, returns React.Element:
() => ReactElement<ImageButtonProps>
You want it to take the props, so you need to type in the props:
(props: ImageButtonProps) => ReactElement<any>
Or
React.ComponentType<ImageButtonProps>
Also, TypeScript doesn't support typechecking ReactElement, so there's no need for doing ReactElement<ImageButtonProps>.
Also see https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html
I'm working with NativeBase (the package name doesn't matter) with Typescript.
I'm creating a simple Input component that wraps the NativeBase TextInput. I added a few own properties and spread all other properties to the NativeBase TextInput.
It means that the props object should contain my custom properties and the NativeBase TextInput.
My question is: How to describe it properly via typescript without copying the NativePase properties?
import { Input } from 'native-base';
type Props = {
// my custom prop
style: object;
// props from NativeBase
onFocus?: () => void;
onBlur?: () => void;
}
export class TextInput extends Component<Props, State> {
render() {
// without these callbacks in Props typescript will complain
const { style, onFocus, onBlur, ...otherProps } = this.props;
return (
<Input style={this.getStyle()} {...otherProps}/>
);
}
}
I tried to use type intersection but it doesn't work because the Input isn't a 'type' in general;
type Props = Input & {
// my custom prop
style: object;
}
also, I tried to extract Input types via typeof. Didn't help.
type Props = typeof Input & {
// my custom prop
style: object;
}
So is there exists a way to avoid copy-pasting the package possible props that I want to use?
Thanks for any help!
If you want to add typing to a third party JavaScript package you create TypeScript type definitions. These are .d.ts files you might have seen in some projects.
There is a Github repository called DefinitelyTyped that comes with third party type definitions for many JavaScript modules. They also have a guide how to write and use your own type definition file.
I'd like to give my React component props a generic type but this is being lost when I wrap it in a higher order component (material-ui) how do I pass along the required information?
type Props<T> = {
data: Array<T>;
}
class MyComponent<T> extends React.Component<Props<T>> {
const StyledComponent = withStyles(styles)(MyComponent)
Using <StyledComponent<myType gives an error as it doesn't know about the generic.
My bet is to annotate your new component like this:
const StyledComponent: <T>(props: OuterProps<T>) => JSX.Element = withStyles(styles)(MyComponent) ;
Mind you probably need to differentiate OuterProps and InnerProps, see below example I made for reference:
https://stackblitz.com/edit/hoc-generics
Hope that helps!