Usually, I write and use custom function components, in one way or other, in this general format:
import React from 'react';
export type MyInputProps = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement & MyInputFieldProps<T>>, HTMLInputElement>
export const MyInput = React.FunctionComponent<> ({
children,
...props
}: MyInputProps) {
return (
<>
{children}
<input {...props} />
</>
);
};
// Pay attention to how I use <MyInput> without generics. The rest of the details aren't important.
export const MyInputForm: React.FunctionComponent<{}> = (props) => {
return (
<form>
<MyInput
name="test"
value="test"
onChange={(event) => {
const name = event.target.name;
}}
/>
</form>
);
};
Sometimes, I find that the typings just aren't good enough. I want to convert the component type into something generic in order to leverage TypeScript typings and Visual Studio Code Intellisense, such that below will work:
<MyInput<'myFieldName', string>
name="myFieldName" // It can't be any other string.
value="myFieldValue" // Numbers will cause type errors.
onChange={(event) => {
const name = event.target.name; // string
const value = event.target.value; // string
}}
/>
So, what do you do when you need to inject the types? Use generics.
But then, I run into this:
// TypeScript won't accept this. Neither does Intellisense.
export const MyInputForm: React.FunctionComponent<MyInputProps<Generics>> = <Generics>(props) => {
// ...the implementation
};
If you really want generics on your function component, ditch React.FunctionComponent<YourComponentProps>
and fat arrow functions (() => {}
).
Switch to a format that works. A bird's eye view:
export const YourComponent = function <T>(props: YourComponentProps<T>) {
return (
// ...your JSX here.
);
};
It's no React.FunctionComponent
(it's actually more like () => JSX.Element
), but it'll do!
See MyInput.tsx for an example.