doberkofler
doberkofler

Reputation: 10351

How to correctly type a generic React function component in TypeScript

In the following TypeScript Playground example I tried to generalise the function component Element into a GenericElement component but TypeScript complains about the syntax.

How to correctly type a generic react function component in TypeScript using the React.FC type definition approach?

import React from 'react';

type PropsType = {
  id: string,
  value: string,
};

type GenericPropsType<keyType> = {
  id: keyType,
  value: string,
};

const Element: React.FC<PropsType> = ({ id, value }) => {
  return <div>{id.toString()}={value}</div>;
};

const GenericElement: React.FC<GenericPropsType<keyType>> = <keyType = string>({ id, value }) => {
  return <div>{id.toString()}={value}</div>;
};
Type 'Element' is not assignable to type 'FC<GenericPropsType<any>>'.
Type 'Element' provides no match for the signature '(props: PropsWithChildren<GenericPropsType<any>>, context?: any): ReactElement<any, any> | null'.
Cannot find name 'keyType'.
Property 'keyType' does not exist on type 'JSX.IntrinsicElements'.
Cannot find name 'id'.
Left side of comma operator is unused and has no side effects.
Cannot find name 'value'.
Cannot find name 'id'.
Cannot find name 'value'.
Identifier expected.
Unexpected token. Did you mean `{'>'}` or `&gt;`?
Expression expected.
Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
JSX element 'keyType' has no corresponding closing tag.
'</' expected.
'Element' is declared but its value is never read.

Upvotes: 0

Views: 3438

Answers (2)

doberkofler
doberkofler

Reputation: 10351

Based on the explanation of @Aleksey L., I came to the following complete example that might be helpful to others:

import React from 'react';
import ReactDOM from 'react-dom';

type GenericPropsType<keyType> = {
  id: keyType,
  value: string,
};

const GenericElement = <keyType extends string | number = string>({
  id,
  value = ''
}: GenericPropsType<keyType>): JSX.Element => {
  return (<div>{id}={value}</div>);
};

const UseGenericElement: React.FC = () => {
  return (<div><GenericElement id={4711} value="4711" /></div>);
};

ReactDOM.render(
  <div><UseGenericElement /></div>,
  document.getElementById('root');
);

Upvotes: 0

I think it works using Higher-order function:

import React, { FC } from 'react';

type GenericPropsType<T = any> = {
  id: T,
  value: string,
};

const HoC = <T,>(): FC<GenericPropsType<T>> => (props) => <div></div>

const WithString = HoC<string>() // React.FC<GenericPropsType<string>>

Drawback: you have function overhead only because of type

I don't believe my answer is helpful, because you should explicitly define generic type, or you need to pass an argument in order to infer it:

const HoC = <T,>(a:T): FC<GenericPropsType<T>> => (props) => <div></div>

const WithString = HoC('a') // React.FC<GenericPropsType<string>>

Otherwise, I suggest you to use @Aleksey L.'s solution.

Upvotes: 1

Related Questions