Video Summary and Transcription
The video talk delves into the benefits of combining TypeScript with React to enhance development. It starts by explaining how TypeScript generics can be used effectively in React components and the importance of TypeScript interfaces. The speaker highlights the advantages of using TypeScript for both function and class components in React, such as improved error detection and type safety. The talk also covers advanced patterns like discriminated unions and intersection types for more complex props scenarios. For those looking to migrate from Flow to TypeScript, the speaker mentions useful libraries and tools. Additionally, the video provides insights into how TypeScript can make refactoring easier and more reliable. The speaker offers resources such as remote workshops and a cheat sheet for common TypeScript and React situations, available on his website.
1. Introduction to TypeScript and React
Welcome to TypeScript plus React equals love. I'll be showing off TypeScript features that can prevent bugs in our React apps. Visit BenMVP.com for the slides. My name is Ben Alegbedo, a Christian, husband, and father. I live in the San Francisco Bay Area and work as a principal front end engineer at Stitch Fix. Let's dive into this react plus TypeScript business.
Let's go take a look! Well, hello there! Welcome. First off I want to say happy fifth anniversary to React Summit! Happy Birthday! And welcome to all of you to TypeScript plus React equals love. You know, I kind of actually wish that I would have used a fire emoji instead because that's how awesome, I think, the partnership is between TypeScript and React. But I stuck with the heart emoji.
So I want to spend our time showing off TypeScript features that can prevent bugs in our React apps. OK? So I'm assuming that you have developed in React before, but you know little to know TypeScript. Even if you do know lots of TypeScript, you'll get lots out of this. But for those that don't know TypeScript, I'll be explaining the concepts as we go. And just so you know, these slides, they're already online. If you visit my site, BenMVP.com, you'll find a link there. Or you can follow the Bitly link that's at the bottom there.
All right. So just to introduce myself formally, my name is Ben Alegbedo. I am a Christian, a husband and a father. Real quick, this is my family. That's my wife, Rashida. We've been married 10 years, just last September. That's our oldest daughter, Symone. She is six and a half. Our middle daughter, Avery, who just turned four last month as well. And at the bottom is our son. He's Asher. He's a little over a year and a half, and we're still trying to help him smile in pictures. We're still working on it. So we live in the San Francisco Bay Area, a town called Pittsburgh, California. So not Pittsburgh, Pennsylvania, Pittsburgh, California without the H. I am a principal front end engineer at Stitch Fix, and I'm also a Google developer expert and Microsoft MVP, both in web technologies.
All right. So enough about me. Let's dive into this react plus TypeScript business.
2. React Components and Props in TypeScript
A react component is just a function that takes in props and returns JSX. You can use an interface to define the props, and the type of the interface becomes the type of the props argument passed to the app components. Props cannot be used without a definition, which helps catch errors.
So one thing I want to make clear before we begin, though, is that a react component is just a function. There is nothing really special about it. It just takes in props and returns JSX, so it can be treated and typed like any other TypeScript function.
So to start off, you can use an interface to define the props, as you see here, and it is the type of it becomes the type of the props argument that gets passed to the app components. So you can name the interface anything you like. I chose app props here just for an example, but I actually tend to use just props. And you will see that in following slides as we go.
OK, so the first benefit, let's get right to it with TypeScript. Props cannot be used within a component without a definition. So, like, how many times have you had props in a component used without a prop type definition? Right. And in this case, we're trying to use props that loading without defining it in the props above. And that becomes an error. So there are various rules to try to catch these sort of things, but they can be limited. In fact, they're limited in how you call the prop.
3. TypeScript and Unused Props
TypeScript gives you the confidence to remove unused props because it wouldn't have allowed it in the first place. The error messages may seem cryptic at first, but you'll become more familiar with them over time.
So similarly, when you can't you can't pass a prop if it hasn't been defined. Right. So here we're trying to pass counts, but count was it in the interface for app props. So how many times have you seen a prop been passed to a component and it's not in the prop types and it doesn't seem to be used in the code, but you're afraid to remove it because because you're just not sure. Well, TypeScript gives you the confidence that you can remove it because it wouldn't have allowed it in the first place. So this error message at the bottom can seem a bit cryptic. To be honest, property count doesn't exist on type intrinsic attributes and app props. But as you encounter them more and more, you'll get a little bit more familiar and used to them.
4. React Prop Types and TypeScript
React prop types are optional by default. Script interfaces are required by default. TypeScript will complain if you miss a spot or simply mistake a prop. TypeScript prevents laziness and ensures accurate prop definitions. TypeScript is beneficial in refactors.
OK, the next one. So React prop types are optional by default when you list them. So I see lots of examples where prop types are defined, actually, but none of them are marked with is required. But if you look at the code, the props are definitely required, like they're calling.map on an array and things like that. These are bugs waiting to happen. I script interfaces are required by default, so without doing anything special, you're guaranteed that the values will always exist. That's super nice.
So if I call this component, leaving off the required prop count in this case, it will yell at me. And again, it won't compile. So you can use the question mark to denote that a prop is optional, which means that its value is undefined when it's not passed. So now if I omit the count prop when rendering app, in this example, there's no errors because it will be defaulted to two.
OK, so if you change the name of a prop, all the places using it have to be changed as well. So let's say this prop right here highlighted was originally names, but I changed it to players within the component. So you go and you search and replace to fix all of the issues. But did you get them all? How can you be 100 percent sure? What if somebody was doing something crazy and it didn't match your regular expression? Well, TypeScript will complain if you miss a spot. And actually a derivative of this is when you simply mistake a prop. TS will complain immediately as well.
OK, number four, so it looks it's a lot of work to define a deeply nested shape. Right. Even with ES lit rules, we find ways to cheat. There's nothing forcing the prop types to be 100 percent accurate. Well, TypeScript is now going to get in your way and prevent you from being lazy. Prevent us from being lazy. Let me put myself in there, too. But it's also saving you because you have to define exactly what's available. You can't access properties off the user prop unless you define exactly what they are. So if we decide in the address interface to rename is primary to just primary, we'll get TypeScript errors all throughout the app and the app component until we fix those. So, again, TypeScript is being very beneficial in refactors.
OK, so the next one and this one actually is probably my favorite. So with prop types, all you get is prop types that funk for a function.
5. TypeScript and Function Errors
In TypeScript, you have to define both the arguments and return value of a component. If you make changes, TypeScript will error out unless you fix all the affected places. Functions are often called as a result of user interaction, making it easy to miss errors during manual testing.
Right. There's nothing that tells a user of the component what parameters it will pass when it's called or if it expects something to be returned. Right. All you get is that. Well, now in TypeScript, you have to define both the arguments as well as the return value. And once again, if we decide to add a second parameter to on change, for instance, or change the types of the parameters, TypeScript will error out unless we fix all those places. This happens all the time. Right. How many times have you forgotten to change a functional handler and some places? Right. And the thing is, about functions is that they're usually called as a result of a user interaction, meaning you're less likely to hit this error while manually testing. So now you're relying on your great test coverage to catch these errors. And, you know, we know how that goes. Right.
6. Advanced Pattern: Dependent Props
Sometimes you have a component with dependent props. For example, a text component that truncates text with a truncate prop and provides a show expand prop. To ensure a better developer experience, we want to make the show expand prop dependent on the truncate prop. We can achieve this by defining common props, using a discriminating union for the truncate prop, and creating an intersection of common props and truncate props. Finally, both truncate and show expanded are typed as optional Booleans.
OK, so let's take a look at an advanced pattern. Right. Sometimes you have a component and it has dependent props. So let's say you have this text component that allows you to truncate text with a truncate prop. Right. And it also has a show expand prop to provide a link, a little link to click and expand the truncated text. Well, the show expand prop doesn't really make sense without the truncate prop. So you want to make that configuration an error. Right. It's much better developer experience for users of the text component. If that's the case.
So exactly how do we make this possible? How do we get this error at the bottom for those invalid configurations? Well, there are a couple of ways that you can set this up. And this here is my preferred approach. So first you define your common props, which are the props that will always exist no matter what. Right. Then next, the truncate props type is what's called a discriminating union. OK. Fancy term. But it just means a number of objects combined together one or the other. So then within that first is for the truncate prop for when the truncate prop is false. Or undefined, so it's not specified. So in this case, you were going to set show expand to be undefined. The translation basically is that show expand cannot be set when truncate is false or undefined. All right. And then second is for when the truncate prop is specifically set to true and only true. So when it's turned on, in this case, show expanded is an optional Boolean. We're now allowed to make this extra configuration. So then props is the intersection or the combination of common props and truncate props. It's basically all of the props together. So then finally, in the code, both truncate and show expanded are typed as optional Boolean.
7. Type Safety and Advanced Pattern
You can use props as needed in the code and ensure their existence. Let's explore another advanced pattern for type safety. We want to support all button element props and ensure type checking. Currently, we rely on React runtime errors for validation. Let's make this type safe by defining new props and intersecting them with button element props. If there is a conflict, we override the variant and size with the new props.
So you can use them as you need inside of the code and check to make sure that they exist as necessary.
OK. So then let's look at another advanced pattern. Let's say I've got my button component, right. That's a wrapper over HTML and HTML button. It has some props to control the visual design at the top like variant in size. But I also want to support all of the button elements, props like being able to pass in type on click, disabled, et cetera, et cetera, et cetera. And of course, I want them all type checked.
This is all about type safety. So we already do this sort of thing without types. TypeScript, we pass along a whole bunch of unknown props to an underlying button element. But there is no validation in just vanilla JavaScript. So I could pass literally any prop and we're relying on runtime errors from React to tell us like, oh, wait, wait. You cannot pass an H ref attribute to a button element. And it will complain in the error console.
Well, let's try to make this type safe. Instead, this is what our TypeScript definition could look like. Again, there are a number of different ways that we can accomplish this, but this is the way that I like to accomplish it. All right. So first, you define whatever are the new props. And I'm calling it new props as the interface. So in this case, this will be variant and size. These are the stuff that's on top of what a button element has. Then we want to define props that type as the intersection of new props and all of the button element props. Right. But there may be a chance that the button element already has a variant or size prop in it. So here's the catch. In which case we want to override. Right. The variant and size with the ones that are in new props.
8. TypeScript and Button Element Props
But when there are name collisions and interfaces and types, weird things happen in TypeScript. We want all of the button element props, except the new ones that we're defining. In the component code, we can spread button props like we always do, except now button props is fully typed. Lastly, let's say you have a list component that has a render prop for each item. The list needs to be able to handle different types. We want to be able to know the type of the item param in the render prop based upon the type of the item's prop. Here's how we make it happen.
But when there are name collisions and interfaces and types, weird things happen in TypeScript. I'm not quite sure what those are yet, but I know that they happen. So we want all of the button element props, except the new ones that we're defining. Right. So that's what this line is doing. We use keyof to get all of the prop names out of new props. That would be variant. That would be size. Then we remove or omits those props using the omit utility generic. So we remove them from the button element props. Then finally, we merge those in to the new props and we have our type for our props for button.
Then finally, in the component code, we can spread button props like we always do, except now button props is fully typed. It knows that type is in there. Onclick is in there. All of the attributes that a button has. Now, the users of button though, wouldn't be able to specify an href prop, for instance, or some other prop that doesn't exist because that is not on the type. We would get an error.
Lastly, my last feature that I want to show you is, let's say you have a list component that has a render prop for each item. But, list is generic so it doesn't know what sort of items it's getting because it doesn't really care about what items it's getting. All it's doing is just displaying it and then maybe having dividers in between or something like that. It's the render prop that does the work of rendering the actual UI of the items. So, my biggest beef with render props in general is that I literally have no idea of what I'm getting when I'm not using TypeScript. So, now with TypeScript, in this case, the list needs to be able to handle different types. So, on the left, we pass an array of strings and we call .length on each item. On the right, we have an array of numbers and we call .toFixed on the data just to prove that we're calling different properties. So, we want to be able to know the type of the item param in the render prop based upon the type of the item's prop, right? We have to have that relationship. And, in this case, we're not typing what item is, so it's all based upon what's in items. So, you can imagine if items were an array of objects, let's say, how much more necessary this sort of functionality would be. So, here's how we make it happen. Like I said, a render prop is just a special function that happens to return React.
9. TypeScript Generics and React Resources
But it can be typed just like any other prop function. The list component first defines a generic parameter T. It then passes T over to the interface. The render prop is going to be passed items of type T. Generics are critical and helpful in shareable code. TypeScript generics for people who gave up on understanding generics. The React TypeScript cheat sheet provides recipes for common situations. TypeScript works for both function and class components. I periodically host remote React workshops called mini shops, including TypeScript for React developers. Visit benmbp.com for more information.
But it can be typed just like any other prop function. And, with the power of generics, it can be generically typed. So, the list component first defines a generic parameter T. That's in the angle brackets there. It then passes T over to the interface. So, props of T. Which, and then third, it says that the items are an array of these T types. We don't know what T is, but it's a generic type. And then next, the render prop is going to be passed items of type T. So, T is malleable. It can change depending on how the component is rendered. So, when I render a list and pass strings, T is now a string. But when I render a list and pass numbers, T is now a number. So, list is generic, or parametrized as I call it. So, one quick thing I want to note is that this little angle bracket T comma bit, it's a little weird and not normally what you would do with generics, but the comma is necessary when you define a component using arrow functions. Otherwise, the parser can't tell the difference between JSX or an arrow function. So, generics are pretty mind-bending at first, as you might be feeling if you've never seen them before, but they're also really critical and helpful in shareable code. So, I've included this link at the bottom as a resource for you. TypeScript generics for people who gave up on understanding generics. Pretty epic.
Okay, so I've got some other resources here as well for you. The most helpful one will likely be the one at the top, the React TypeScript cheat sheet. It provides lots of recipes for common situations for you. I also only talked about function components, but TypeScript does work for class components as well. I mean hooks are the future, so you should be using functions. But if you want to use classes, some of these resources have examples for you there.
So before I finish, though, I periodically host a series of short three-hour remote React workshops. I call these mini shops and one of them is called TypeScript for React developers. So if you're interested in a more hands-on learning of TypeScript plus React doing exercises and that such, you should check it out. Just visit my website, benmbp.com, and you'll see them listed there.
10. Free Giveaway and Conclusion
I have others as well that you see there, zero to React with hooks, which is an intro migrating from class components to React hooks and such. I am doing a free giveaway for the conference attendees to celebrate this fifth anniversary of React Summit. So if you go to my website again, benmbp.com, go to the mini shops page, find one, one mini shop that you like and that you can attend and send out a tweet with the link of it. Tag me in it so I know you sent it out and I'll pick one to give a free ticket. And bonus points, if you can actually include a selfie of you watching this talk or during the Q&A. So hopefully you found it all insightful and it's motivated you to use TypeScript in your next project or maybe even in your current React project. The slides are already available online. Visit bennvp.com. If you've got questions, feel free to reach out to me on Twitter, at bennnvp, and we can chat there. Thank you so much. I am a huge TypeScript fan, so I always appreciate a good TypeScript talk.
I have others as well that you see there, zero to React with hooks, which is an intro migrating from class components to React hooks and such.
So what I want to let you know, though, is that I am doing a free giveaway for the conference attendees to celebrate this fifth anniversary of React Summit.
So if you go to my website again, benmbp.com, go to the mini shops page, find one, one mini shop that you like and that you can attend and send out a tweet with the link of it.
Tag me in it so I know you sent it out and I'll pick one to give a free ticket.
All right. And bonus points, if you can actually include a selfie of you watching this talk or during the Q&A.
All right. Free giveaway there.
All right. So that's it.
I know. I just flooded you in 20 minutes with a whole bunch of information.
So hopefully you found it all insightful and it's motivated you to use TypeScript in your next project or maybe even in your current React project.
That would be awesome.
So, again, the slides are already available online.
Visit bennvp.com.
I keep sending you there.
The slides are there.
Or you can follow that Bitly link.
If you've got questions, feel free to reach out to me on Twitter, at bennnvp, and we can chat there.
All right.
Thanks.
And I hope you enjoy the rest of the conference.
Thank you so much.
I am a huge TypeScript fan, so I always appreciate a good TypeScript talk.
We actually have a ton of questions from the audience.
So I'm going to just jump right in.
First of all, your kids are super cute.
Well done.
Thank you.
11. TypeScript and Rest Props
Rest props are always typed in TypeScript, ensuring safety in your code.
Thank you very much. All right. So somebody asked, how about dot dot dot others that get spread blindly to subcomponents, I assume is referring to a prop? So that's the ability to be able to type the rest of the parameters that are passed to your props. So I talked about it a little bit at the end there that there is a way to type those sorts of things. So you have to be able to communicate to TypeScript. Let it know that it's a. This is different situations. So if you define eight props and you only pull out three props, the rest props will have the other five. So those are always going to be types. So anything in TypeScript is type. There's no longer a junk drawer of objects. So there are various different ways that you can make that more sophisticated. But the result is that rest props are always typed. Safety first.
12. Using Types and Interfaces for Props
When using TypeScript for props, there is not much difference between types and interfaces. It comes down to preference. I tend to use interfaces for base props, but types are useful for more sophisticated props like discriminated unions.
All right. Our team asks when would you use a type for your props and when would you use an interface? So I get this question all the time. Right. And I think this is maybe a just a problem with TypeScript. Maybe I'll call it not even a problem, but just a confusion with TypeScript. And even on their docs where they talk about TypeScript versus interfaces, they go into all this nuances about the differences. And basically what it is, is ninety five, ninety six, ninety eight percent of the time there's no difference. You can do the exact same thing with interfaces that you can do with types. So it really comes down to preference. I tend to use interfaces for my props just because that's how I learned it. And it kind of makes sense in my mental model, but you can totally use types as well. And you can extend types just like you can extend interfaces, just a different syntax. But once you start using more sophisticated props and maybe discriminated unions like I was showing there, you'll end up using types. But for the base ones, I just use interfaces.
Cool. So, to answer the question, not much difference. But interfaces are the popular kid. At least to me and most of the docs that I see. Same. So, we both say it, so then that must be the way that it is. Of course, because we're the experts, right? Exactly.
13. Migrating to TypeScript and Flow Conversion
Migrating to TypeScript from no TypeScript should be done incrementally. Start with files that have no dependencies and work your way out. Converting from Flow to TypeScript can be challenging, but there are libraries like Flow to TypeScript and tools like the one from Airbnb that can help automate the process.
This is – oh, I love this question. I'm sure you get it all the time. Do you have a take about migrating to TypeScript from no TypeScript, but also from Flow? Yes. Okay. So, that's interesting. So, from no TypeScript, my suggestion is always to migrate incrementally. So, don't do a whole big rewrite with one PR that's like, we are now on TypeScript. Nothing like that, please. So, if you're going to do it incrementally, the easiest approach is to take the files that have no dependencies, that everything else depend on, and start with those, and then work your way out until basically you get to the outer app. Because it's much easier to import a JavaScript, it's much easier to import a TypeScript file from a JavaScript file than the reverse, because a TypeScript file needs the type information, so work your way out, and that's a good strategy. Going from Flow is actually something interesting that I haven't thought about in a while. So I wrote one of my original apps in Flow years ago, and I recently tried to convert it to TypeScript. I did it manually now, and it didn't, it didn't really go so well, but that was mainly because there were some big differences between Flow. It was really old Flow, too, so, but I've seen some, there's some libraries, there's a library from Khan Academy. It's called Flow to TypeScript basically, or Flow to TS, something along those lines that they, folks can look at, so there may be a way to automate it as well. One last thing, I just heard about, there is a tool from Airbnb, don't know the name of it, but I'm going to look it up, that goes from vanilla JavaScript to TypeScript, so it takes your vanilla JavaScript, puts it into an AST, Babel AST, more or less, and then converts it to TypeScript. So who knows how readable that TypeScript is? I have to take a look and see what it's like, but that's also an option to kind of jump start your TypeScript journey.
14. Suitability of TypeScript for Projects
TypeScript is not suitable for libraries that shouldn't be transpiled, but for everything else, it's a good candidate. The decision to use TypeScript depends on the team's experience. For small projects, it may not be worth learning TypeScript, but for larger projects with more people, it makes sense to learn TypeScript. Once you know TypeScript, there's no reason not to use it.
Well, if a computer made it, who knows? All right. Melanie asks, what project is TypeScript not suitable for, if any? Melanie, so I think, okay, so I think the only thing that TypeScript is not suitable for is a library that shouldn't be transpiled in general. So it has nothing to do with TypeScript but you're writing some node script or whatever and it needs to be as small as possible because it's used all the time, then you probably just want to write it in vanilla ES5 and not have any transpiling happen. Other than that, I would say TypeScript is a good candidate for anything. It just depends on the experience of the team as to whether they know TypeScript. So if it's something really tiny and people don't know TypeScript, then it doesn't really make sense to learn TypeScript for that tiny thing. But if it's something tiny and I'm writing it, I'm going to write it in TypeScript because I already know it. The learning curve is already—I've already made that journey up the learning curve. So the bigger the project, the more people on the project, the better it makes sense to learn TypeScript for the first time, I would say. So that might be kind of the question behind what she was asking. But in general, once you know TypeScript, there's no real reason to not use it for anything.
Comments