This guide will provide you with the syntax you need to properly define the props entering your components, compare and contrast defining props as a class
or interface
type, and give you a helpful tip for when you need to provide default values to optional props.
Typescript brings some awesome features that extend JavaScript in powerful ways, including the ability to define the structure of an object in a variety of ways. In your search for the best way to define objects, you will undoubtedly encounter a variety of options, class
and interface
being the most common. If you take a look at Typescript's documentation, you can research for yourself the difference between these common object definers. This guide will give you the short version.
class
is for when you want to define more than just the structure of an object:
1class Greeter {
2 greeting: string;
3 constructor(message: string) {
4 this.greeting = message;
5 }
6 greet() {
7 return "Hello, " + this.greeting;
8 }
9}
10let greeter = new Greeter('world')
interface
is for when you want to enforce structural contracts
(i.e what you want passed in or what you want returned back):
1interface FullName{
2 firstName: string;
3 lastName: number;
4}
5
6const fullNameObj:FullName = {
7 firstName: "Jon",
8 lastName: "Snow"
9}
So which should you use to define a prop? If you said interface
, you would be correct.
Whether you're coming in fresh to using Typescript with React or are a grizzled veteran looking to add more functional components to your codebase by introducing hooks, it's important to know the different ways to define props.
One of the ways you can define props is simply by defining them in the parameter list of a function as demonstrated above.
For example:
1interface FullName {
2 firstName: string;
3 lastName: string;
4}
5function FunctionalComponent(props:FullName){
6 // props.firstName
7 // props.lastName
8}
In most cases, the simple way is fine. But what if you want to define default props if they aren't passed in? Or you just want to have cleaner syntax within your component?
For those cases, you can leverage a JavaScript syntax feature known as destructuring. This allows more flexibility in how you can define your props.
1// Using the same FullName interface from the last example
2function FunctionalComponent({firstName, lastName}:FullName){
3 // firstName
4 // lastName
5}
What if you wanted to add a middle name? Not everyone has a middle name, so you want to make it optional. Destructuring can provide a clean solution to that problem.
1interface OptionalMiddleName {
2 firstName: string;
3 middleName?: string;
4 lastName: string;
5}
6function Component({firstName, middleName = "N/A", lastName}:OptionalMiddleName){
7 // If middleName wasn't passed in, value will be "N/A"
8}
Now whenever you need to reference middleName
, if it doesn't exist you get a nice default "N/A".
Accomplishing the same functionality using the non-destructured syntax would look something like this:
1// using the same OptionalMiddleName interface from above
2function Component(props:OptionalMiddleName){
3 if(!props.middleName){
4 props.middleName = "N/A"
5 }
6}
Not awful, but not exactly nice either.
As always, though, beautiful code is in the eye of the beholder (sometimes).
Another way to define props is to import and use React's Functional Component
type, FC
for short.
Using React.FC
is more verbose, but does have some added benefits:.
displayName
, defaultProps
)children
:1 const ReactFCComponent: React.FC<{title:string}> = ({children, title}) => {
2 return <div title={title}>{children}</div>
3 }
You can read more about the benefits of React.FC
here
Which should you use? The React community generally frowns upon using React.FC
due to its verbosity, and because its benefits aren't worth the trouble and difficulty to read.
Hopefully this simple guide provides you with a good reference for you to work from. In general I like to destructure all my props entering the component, as it provides a clean and effective syntax to work with. Good luck in your Typescript React journeys!