0

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.
8
  • 1
    You can't use React.FC in this case. typescriptlang.org/play?#code/… Commented May 6, 2021 at 7:32
  • 1
    Regarding extends { toString(): string } - it is generic type parameter constraint, so provided type must have toString method (you're doing id.toString() within the function). Regarding arrow function: if you use React.FC you can't leave generic type parameter "open" (provided by consumer) Commented May 6, 2021 at 13:39
  • 1
    Here's example defining component as an arrow function typescriptlang.org/play?#code/… Commented May 6, 2021 at 13:40
  • 1
    @doberkofler one more thing - not sure why generics needed here. Why not define id: string | number for example? Commented May 7, 2021 at 6:39
  • 1
    @AlekseyL. You are absolutely correct. This is s contrived example only to keep it as simple as possible. Commented May 7, 2021 at 8:33

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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');
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.