202

I am learning TypeScript and some bits are confusing to me. One bit is below:

interface Props {
  name: string;
}

const PrintName: React.FC<Props> = (props) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

const PrintName2 = (props: Props) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

For both functional components above, I see TypeScript generates the same JS code. The PrintName2 component seems more streamlined to me as far as readability. I wonder what's the difference between the two definitions and if anyone is using second type of React component?

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Kuldeep Bora
  • 4,072
  • 3
  • 12
  • 16
  • 4
    Answered here https://stackoverflow.com/questions/44133420/what-is-the-typescript-return-type-of-a-react-stateless-component It's more about properties you can find in React.FC. – apincik Jan 30 '20 at 15:02
  • https://www.harrymt.com/blog/2020/05/20/react-typescript-react-fc.html – zloctb Mar 25 '21 at 10:12

5 Answers5

175

Thanks all for the answers. They are correct but I was looking for a more detailed version. I did some more research and found this on React+TypeScript Cheatsheets on GitHub.

Function Components
These can be written as normal functions that take a props argument and return a JSX element.

type AppProps = { message: string }; /* could also use interface */

const App = ({ message }: AppProps) => <div>{message}</div>;

What about React.FC/React.FunctionComponent? You can also write components with React.FunctionComponent (or the shorthand React.FC):

const App: React.FC<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);

Some differences from the "normal function" version:

It provides typechecking and autocomplete for static properties like displayName, propTypes, and defaultProps - However, there are currently known issues using defaultProps with React.FunctionComponent. See this issue for details - scroll down to our defaultProps section for typing recommendations there.

It provides an implicit definition of children (see below) - however there are some issues with the implicit children type (e.g. DefinitelyTyped#33006), and it might be considered a better style to be explicit about components that consume children, anyway.

const Title: React.FunctionComponent<{ title: string }> = ({
  children,
  title
}) => <div title={title}>{children}</div>;

In the future, it may automatically mark props as readonly, though that's a moot point if the props object is destructured in the parameter list.

React.FunctionComponent is explicit about the return type, while the normal function version is implicit (or else needs additional annotation).

In most cases, it makes very little difference which syntax is used, but the React.FC syntax is slightly more verbose without providing clear advantage, so precedence was given to the "normal function" syntax.

Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
Kuldeep Bora
  • 4,072
  • 3
  • 12
  • 16
  • Hey Kuldeep, first of all thanks for the detailed answer. Maybe it is missing a link for "See this issue for details ". If I'm mistaken please disregard :) – Rafael Lebre Aug 04 '20 at 23:01
  • 1
    Hey, the link for detailed explanation is the github link I mentioned in first paragraph. :) Thanks – Kuldeep Bora Aug 23 '20 at 07:53
  • Since React 18 an implicite `children` prop is removed. See: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210 – Michał Styś Oct 17 '22 at 08:22
  • How do you access the children from now on? Should I manually add a `children: ReactNode` property in my type definition? – babaliaris Jan 11 '23 at 22:33
82

React.FC is not the preferable way to type a React component, here's a link.

I personally use this type:

const Component1 = ({ prop1, prop2 }): JSX.Element => { /*...*/ }

Short list of React.FC cons:

  1. Provides an implicit definition of children, even if your component doesn't need to have children. That might cause an error.
  2. Doesn't support generics.
  3. Doesn't work correctly with defaultProps.
76484
  • 8,498
  • 3
  • 19
  • 30
Alexander Kim
  • 17,304
  • 23
  • 100
  • 157
  • 3
    How to you type check prop1, prop2 etc? – Arafat Zahan Aug 07 '20 at 20:27
  • @ArafatZahan You can write `{ prop1, prop2 }: { prop1: string, prop2: number }`. See https://stackoverflow.com/questions/53329592/typed-function-parameters-using-destructuring-and-rest-in-typescript – mic Oct 25 '20 at 18:53
  • or `{ prop1, prop2 }: Props` – mic Oct 25 '20 at 19:07
  • 23
    Use `React.VFC` if you do not use the 'children' prop – KJ Sudarshan Jul 15 '21 at 04:42
  • 1
    @KJSudarshan awesome note, should have more. upvotes or an answer – Joe Lloyd Aug 20 '21 at 10:14
  • 1
    1. Every react element has `children`, look at `React.createElement`. If you don't want them, don't render them. Or use `React.VFC`. 2. It is generic by design xD. But any way it is not a problem of `React.FC`, it is a problem of `.tsx` syntax. It fails if you want to make an lambda function with generic property. 3. `defaultProps` it is runtime part. Setting props from some external magic source is brrrr.. I will never understand this, because I can set defaults right in component. – Profesor08 Oct 21 '21 at 21:27
  • React.FC supports generics, you can do React.FC – danivicario Nov 10 '22 at 15:58
  • 3
    Addressing some of the comments on this answer: As of React 18 React.VFC is deprecated. https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59882 – Ben.12 Jan 26 '23 at 16:48
36

Best solution:

The second approach + a return type

const PrintName2 = ({ prop1, prop2 }: Props): JSX.Element => { /** */}

This gives you complete control over the Props and is more explicit (there is no hidden magic).

Link to GitHub PR to remove React.FC

Kent C Dodds has some thoughts why this is here

Sub-optimal solution:

The first version (React.FC) will add a few types for you - they come from the React Typings

interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement | null;
  propTypes?: WeakValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}

This approach is useful for a few reasons but an obvious one is if your component has children, then you don't need to manually add a children prop. It's not great because there are issues with default props and many components don't have children.

Jonathan Irwin
  • 5,009
  • 2
  • 29
  • 48
  • 1
    This is no longer the case, React.FC no longer includes the children prop! :D – cdimitroulas Jan 17 '23 at 13:23
  • @cdimitroulas would you recommend using React.FC now? – Jonathan Irwin Jan 18 '23 at 15:38
  • 1
    I have been using it everywhere, yes. I think it works great and gives good type safety to ensure you always return a valid value from all code paths. There is just one issue which is that for some reason it doesn't allow you to return a plain string from a component which should be valid – cdimitroulas Jan 19 '23 at 12:02
14

Since you are using React and TypeScript, you should always use the first pattern, as it will ensure that your component is more strictly typed since it implies that the PrintName will be of type React Functional Component, and it takes in props of type Props.

const PrintName: React.FC<Props>

You may read the full interface definition for Functional Components on the React TypeScript typings repository.

The second example you have provided does not provide any form of typings, except that it is a function that takes a set of parameters of type Props, and that it can return anything in general.

Therefore, writing

const PrintName2 = (props:Props)

is akin to

const PrintName2: JSX.Element = (props:Props) 

as TypeScript is definitely unable to automatically infer that it is a Functional Component.

wentjun
  • 40,384
  • 10
  • 95
  • 107
  • OK. but return type in second case should be inferred. hovering over it shows below: const PrintName2: (props: Props) => JSX.Element – Kuldeep Bora Jan 30 '20 at 15:30
  • 2
    While it will be inferred, sometimes inference is not fully correct but also if you add the type information first before you add the return value, you will get errors *until* you meet the return type specification. – crashmstr Jan 30 '20 at 15:32
  • 1
    It may infer it as a jsx element, but it does not automatically infer it as a functional component – wentjun Jan 30 '20 at 15:36
  • 2
    Best answer, straight forward – dman Feb 02 '21 at 17:38
4

Don't use React.FC anymore. The reason why people used to use React.FC was because it added more types like children to your props.

in your code example you don't need children so just write it like this

const PrintName: (props: {name:string; priority: "bold" | "normal"}) => {
  return (
    <div>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}>
        {props.name}
      </p>
    </div>
  )
}

but the problem you will eventually have is that you will want to pass in children to your components and the preferred way of doing this is like this

interface Props extends React.PropsWithChildren {
   name: string
   priority: "bold" | "normal"
}

const PrintName: (props: Props) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p style={{ fontWeight: props.priority ? "bold" : "normal" }}> 
        {props.children}
      </p>
    </div>
  )
}

extending with React.PropsWithChildren is the same as if you typed:

interface Props {
   name:string
   priority: "bold" | "normal"
   children?: ReactNode
}

React.PropsWithChildren can also be passed a generic but the default is unknown. If you want you can set children like this, but unless you are sure you only want a specific type I would just use the default.

React.PropsWithChildren<Button> 

and pass in the type of children you want in your component.

You do not have to pass a return type of JSX.Element, because it's inferred.

Richard Torcato
  • 2,504
  • 25
  • 26