We are going to start refactoring our CountDisplay
component. It is a small stateless component but it has a few props that can benefit from type safety.
There are three ways to type a component, inline, alias, and as a function expression. The inline typing adds a bit of noise to our code and can make it difficult to parse right out of the gate. To fix this, we use a type alias that reads a little bit nicer. To add in a function expression, which we get from the React Types that we downloaded, we can declare this variable to have a type of React.FunctionComponent
which takes as a type argument, our props.
Chance Strickland: [0:01] I'm going to start refactoring with CountDisplay component. This is a small stateless component, but it has a few props that can benefit from type safety. To start, I'm going to rename the file to have a .tsx file extension, and now we can start writing some types.
[0:17] We renamed our file and it's now in TypeScript, but we don't have any type errors. That's because we have not set strict to true in our tsconfig. In TypeScript strict mode, TypeScript would warn us that our props did not use explicit types, but since we are going with an incremental adoption strategy, I want the TypeScript compiler to cause less friction in the beginning.
[0:38] For new projects, I do generally recommend setting strict to true. I do think that's a good goal for our project as well, but its flexibility for incremental adoption is one of my favorite things about TypeScript. You get to choose exactly how much type safety is enough for you given your project's constraints.
[0:55] Now that we've changed our file extension, let's write some types. I'll start here by adding some types to my props. We could do that in a number of different ways. The easiest way is by simply typing the props in line with the function declaration itself.
[1:09] This function takes an object that has a count property, and that value should be a number. It also has a className prop that is optional but will be of type string. We could stop here and move on, but for me, the inline types do add a bit of noise to our code, and it makes it a little more difficult to parse right out of the gate, so I like to define a type alias or interface below my component instead.
[1:35] I'm going to call this interface CountDisplayProps. To me, that reads a little bit nicer. Again, we could say we're done and move on, but I do want to show you another way that we could choose to type this component. In an earlier lesson, we installed the React types from DefinitelyTyped as a dependency.
[2:01] One of the types that we get from that package is the FunctionComponent type. If we change our function declaration to a function expression, we could declare this variable to have a type of React.FunctionComponent, which takes as a type argument our props. Now, we can remove our props annotation from the function object itself and works just as well.
[2:28] A few differences I do want to note about this strategy. When we use the FunctionComponent type, we get a couple of extra things aside from typing the props. TypeScript now recognizes CountDisplay as a React component type, and it now gives us some nice help when we go to declare some of its static properties.
[2:45] For instance, if we wanted to set a displayName on this component, we could set a displayName to anything we want, and TypeScript knows that our displayName is of type string.
[2:57] Another feature is that the FunctionComponent type automatically adds a children prop type to our component. That's because most function components do accept children for composition. In our case, our CountDisplay component does not actually accept any children, so we might not actually want this feature.
[3:13] For that case, we can use React's VoidFunctionComponent type, which is functionally the same thing, but it omits children for our type, unless we explicitly declare it on the props type. Both FunctionComponent and VoidFunctionComponent have shorthand aliases of FC and VFC respectively. I'll just go ahead and change this component to use VFC for brevity here.
[3:36] To recap, we've updated our file extension to .tsx. We've created types for our components' props, and we've assigned a type to our component as a safer interface for components that consume it. Thankfully, we did not have any other type errors in our components, so our refactoring for this file is now complete.
Update: VoidFunctionComponent
is now equivalent with FunctionComponent
. Details here.
I finally know what React.VFC means ๐ Thank you Chance! ๐