Best Practices and Advanced TypeScript Tips for React Developers


Are you a React developer trying to get the most benefits from TypeScript? Then this is the workshop for you.

In this interactive workshop, we will start at the basics and examine the pros and cons of different ways you can declare React components using TypeScript. After that we will move to more advanced concepts where we will go beyond the strict setting of TypeScript. You will learn when to use types like any, unknown and never. We will explore the use of type predicates, guards and exhaustive checking. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.

148 min
26 Oct, 2022

AI Generated Video Summary

This workshop on advanced TypeScript tips for React developers covers a wide range of topics, including TypeScript compilation, converting JavaScript components to TypeScript, enforcing mutually exclusive props, and data validation. It emphasizes the importance of strict settings in TypeScript and using generics for type safety. The workshop also highlights the benefits of TypeScript's type inference and its integration with React components. Overall, the workshop provides practical insights and best practices for leveraging TypeScript in React development.

1. Introduction to TypeScript Tips for React

Short description:

Welcome to this workshop on best practices in advanced TypeScript tips for React developers. We're going to cover a wide range of topics, including writing React components using TypeScript, doing fancy and useful stuff, and using TypeScript for compile-time and runtime safety. Prerequisites include having a computer, React and TypeScript experience, and the necessary tools installed. You can find the links to the repository and slides in the provided resources.

Welcome to this workshop on best practices in advanced TypeScript tips for React developers. We're using Discord as the chat for all questions, et cetera. There is a Zoom chat, but that gets lost. So Discord is a little better. You should be able to find on the Desktop where you can find all those questions. I've already pasted a couple of links in there. They're in the slides as well, so you'll see them again. But there are links to the slide deck, which is kind of useful to keep around, as you'll see in a minute, and a link to the GitHub repository, which we're going to use as a starter repository. But I'll come back to that in a minute.

So who am I? My name is Maurice de Beyer. I'm the CTO of React. I'm a component developer on GitHub. You can find me on Twitter, My name's Maurice de Beyer, also known as the Problem Solver, kind of my company name. Microsoft MVP, developer, instructor, amongst other things. Currently working at a financial startup called SoMeday. Pretty interesting stuff we're doing there. Got an email address, et cetera, all not that interesting. What's much more interesting is what we're going to do this workshop. Basically, I'm going to show you how to write React components using TypeScript and relate the things to it, but not just simply take a basic component and write it in TypeScript. We'll do that a bit, but for the most, we're going to go away beyond that, and we're going to see how you can do fancy, interesting, useful stuff. Some of it might not be all that useful for you. But then again, maybe a year from now, you'll come to the project where you'll actually be able to use it. It is opinionated. So you might think, well, some things you shouldn't do, some things you should do. That's perfectly fine. Everyone is entitled to their own opinion. It's my workshop, so in this case, it's my opinion. But feel free to disagree with some points if you think there are better ways of doing it or there are some disadvantages on the way I'm doing it which I'm failing to mention. But some of the stuff we'll look at is doing mutually exclusive props for components, doing generic component prop types, deriving prop types from other components, inferring types, going beyond the standard strict, validating data, which is not strictly type scripts, but inferring the types from that is very much useful type script feature. We'll go into type mapping and we'll go into how we can use type scripts not only for compile time, but also for runtime safety.

A tip, maybe you've seen workshops from me before, but then if you have, you've seen this slide before, I'm gonna provide you with links to all the codes we're gonna do, all the changes I'm gonna make. You can copy them, that's perfectly fine, but you won't remember them quite as well. And in practice, it's like doing is the best way of learning. If you see me do it, you'll remember some of it. If you actually do it yourself, you'll remember a lot more. So highly recommended to do that. Of course, we need some prerequisites. We're not gonna start coding out of thin air. So of course, you'll need some computer, but given that we're online and you're watching this, I'm assuming that part is covered. This is very much focused on developer. So I'm assuming that everyone has at least some React and some TypeScript experience and a working setup. So I'm kind of assuming that nodes, Gits and MPM are all taken care of, but just in case they're not on this slide, you'll actually find links to node, GitHub, et cetera. These are the versions I'm running. Let me actually show how you can get that in a terminal window. You can do nodes-v or dash dash version, and it will tell you what it is. MPM-v and Git-v. You don't need to be running exactly these versions. Something quite a bit older will work just fine as long as it's relatively recent, as long it's one of the support versions of nodes. So if you're on node 10, well, maybe not. I'm not even sure if it would run. I haven't tried, but I'm assuming not. But if you're a node 14 or 16 or 17 or something like that, that's all going to be fine. Same with MPM. I'm using version 8.19. But if you're on version seven, that's going to be fine. And I'm pretty sure version six is going to be fine too. Git version doesn't really matter just as long as you have some Git clients to do a Git clone. As you can see, I'm running Windows, of course, that doesn't matter. If you're on a Mac or a Linux machine, that's perfectly fine. Of course, links in the slides are not much use to you if you don't have a copy of those slides. You can't really click on my slides. So with these two links, you can get a copy of the repository and the slides. So let me quickly go to the slides. And of course that opens up my other terminal. Here it is. As you can see, you get a copy of exactly the same slides. And the interesting thing, if you go here, you'll see lots of codes, samples like this, images. All of those are links. You click on those. You'll actually get to the commit where that change was made.

2. Setting up the Project and Repository

Short description:

In this workshop, we'll be working with a GitHub repository named 'advanced-react-typescript-2022'. The repository contains all the changes we'll be making, with each step corresponding to a commit. We'll be using Next.js, but the concepts are not specific to Next.js and can be applied to other setups as well. After cloning the repository and installing the packages, we'll start the project using the 'next dev' command. The application will run on localhost port 3000.

So you can see exactly what was going on. And if you've got a typo, et cetera, you can check here or like... I know I told you not to copy code, but if you need to, you can copy the code from here. Let me close that again so you can actually see the link. The link above is for the repository which we'll need. So let me open that up if it wants to open up. There it is. Public GitHub repository. So my GitHub name is maurice.db and the repository name is advanced-react-typescript-2022. I'll come back to that in a minute.

So as I mentioned, all the changes are there. Basically we're gonna do lots of steps. I think I've got 15 different steps we're gonna do in this workshow. All of those are committed in this repository as a single commit. So here you see something like displaying types. Well, there's a commit in the GitHub repository for displaying types, which matches exactly the step we're gonna do. So every step is one commit and easy to find what's going on there. You'll also see this gentleman come by quite often. Jean Luc Picard, for this famous make it so meme. That's basically your cue to do something. I'm gonna show you first how to do it, what the goal is, how we're gonna do it, explain some stuff about it, et cetera. And then basically whenever Jean Luc Picard shows up again, it's your turn to actually go and do that. When we're gonna do that, I'm gonna open up breakout rooms so you're in a small group together so you can actually work together if you want to. Those breakout rooms work pretty well in Zoom. Although I know from experience that sometimes people get lost from their breakout rooms. So they kind of drop out. I can see that happening. I will reassign you to breakout room. But the only thing is I don't know which you were originally in. So if that happens, I'll put you back in but it's probably not gonna be the same breakout room. There will be like three or four people per breakout room, so small groups to work. There will be a little countdown timer there. So I'll typically open them for four or five minutes and then you can kind of watch the countdown timer see how much time you've got for that exercise. If you get stuck with an exercise, chat with the other people in the breakout room about it if you want to, or you can invite me into the breakout room and ask me questions about it. Or of course you can look at the GitHub repo and the commits. So different ways to help yourself if you get stuck. All the exercises are independent. So it's not like if you get stuck with one exercise you can't do any of the future ones. They're all basically split outs and we basically start with a clean slate. So if you get stuck with something, it typically won't be a problem for any of the other steps unless you have some major problem where the application doesn't run anymore. But other than that, it shouldn't affect the different steps.

So the first thing we need to do is we need to clone the repository. So I'm going to go back to the GitHub Repo, copy this. I'm using SSH, but it really doesn't matter whether you're using HTTPS or some other way, some tool maybe to clone it. I'm just going to use the command line. So on the command line, I'm going to do a git clone and paste in the repository link. And it tells me cloning advanced React TypeScript 2022. So let's see the folder. And if I check what's there, you should see repository. You'll see a next.config.js. I'm using Next.js here because I really liked Next.js. That said, none of the stuff we're doing here is Next.js specific. It would work just as well with create React app or with some other setup or for that matter. Quite a few things will work outside of React as well with plain TypeScript. Now there is a package.json here, so we need to install the packages. So we'll do an npm install. That's gonna run for a bit of time. Shouldn't take too many or too much time. Like it just took seven time. It took seven seconds on my machine. Of course, I've got all the packages cached because I've done this before so it's probably gonna take a bit longer on your machine but still won't take too long. We're gonna fire things up in Visual Studio Code. Visual Studio Code, again, you don't need to use Visual Studio Code for the exercises just any code editor is fine. If you wanna use Sublime or WebStorm or any of these things, go ahead. I like Visual Studio Code, but it's not a requirement. Then with that, I'm gonna start the dev script. So next dev, you could do it from the command line or do it from Visual Studio Codes or whatever editor you're using. It's gonna start up the project. You can see here, let me, can I zoom in here? No, I can't. But if I click on this, it will start up the application in localhost port 3000.

3. Application Setup and Branch Selection

Short description:

Ensure the application works and navigate to the first step. Switch to the 00.start branch to begin with the incomplete code. Start the application and access it at localhost port 3000. Breakout rooms will be created for assistance.

Where is it? There it is. So you should see this application and you should be able to navigate between pages. Some of these will produce an error. Let's see which one? I think this one. Nope, that one doesn't. Well, some of them are gonna produce an error. Don't recall off the top of my head which one. But that's fully expected. Just make sure that this works and you can navigate to the first step, creating components using TypeScript. All of that works. And any issues with others we'll deal with when we get to those because they're intentional.

So that was the npm install I just did. That's one step I skipped. Wasn't important yet, but we actually want to make sure that we're on the right branch because currently I'm on the main branch and the main branch contains all the finished code which is why it wasn't throwing an error. But if you go to the 00.start branch, oops, didn't want to advance yet, then we'll start with all the workshop code. So gonna go here select 00.start or you can do it from the command line if you want. And now we're on the beginning stages of the workshop where the code is not completed and I should actually be able to create an error somewhere here, if we type script type, yeah. That's kind of like the error I was expecting before but didn't get. But like I said, all of that's intentional and we'll fix those along the way. So that's starting the applications, so either do npm run dev or start using tooling like I just did and navigate to localhost port 3000 to see the application running and then you should see this application. So that's timed for you to do that.

Let me create a BreakoutRooms. Assign automatically. Let me check the time they're set. It's set for four minutes, that's perfectly fine. So that should be more than enough to install the application, the MPM packages and get the application running. And just before I do that, let me copy the link to the repository in the Zoom chat as well for those who don't have access to the Discord channel. And let me do the same with the slides actually. Where did I leave them? That's the slides. So I'm gonna open up the breakout rooms and I'll see you all in a few minutes. And remember if you get into issues, help each other or invite me into the room to help you out.

4. TypeScript Compilation and Type Checking

Short description:

In this section, we address the issue of TypeScript code not being fully type-checked during compilation in modern stacks like Next.js and Create React app. We demonstrate a simple solution by adding a compile script to the package.json file and using the TypeScript compiler (tsc) with the 'no emit' option. Additionally, we introduce the 'watch' option to automatically rerun the type checking whenever a file is saved. For larger projects, we recommend using a pre-commit hook to ensure type errors are caught before committing. These changes provide better visibility into potential type issues and enhance the effectiveness of TypeScript in your codebase.

Okay, and that's everyone back. So any problems getting the application cloned and up and running? No, no, no. Okay, I'm seeing all good in the chat, so. I'm not hearing any problems, so I'm assuming no one has, but if you did, let me know later and I can help you out.

So the first thing we're gonna do is we're gonna take a look and check out at compiling the code. Because it turns out that we're running the application, but not everything is perfect. You add in the console window here, not seeing any issues, the application runs fine. If I checked the console window here, it prints a one, but not seeing any error messages, so looks all good. But the term is out, it isn't.

What happens with modern stacks? And I'm using Next.js here, but exactly the same would be true with Create React app, is that the TypeScript code used is actually stripped off all the additional type information and turned into usable ECMAScript for the browser using Babel, or in the case of Create React app, or SWC in the case of Next.js, or there are actually different ways of doing it. But basically that excludes any types checking. Now Create React app does do some type checking alongside that. Next.js doesn't do that at all. But even with Create React app, it doesn't type check everything. It only type checks code, which is actually included.

So, for instance, your unit tests might contain type errors which you would not be aware of. And the code here actually contains some type errors. If I open up the index.js in the pages, we can see here, I'm not sure if it's all that visible, but you see a red line here and there is a little red block here. And if I go there, we can see that there is a compile error here. And if I hover my mouse over it, it says type number is not assignable to type string. So there is a mismatch. And in this case, that's actually kind of obvious because I'm saying you should type the same thing because I'm saying const, not a string is off type string, so the colon string here, and then I'm assigning one to it, which is of course the number one. So there is clearly a compile issue here, a type issue, but we're not seeing any compile errors, any type mismatch errors anywhere until I actually open this file. And now the filename here in the Explorer shows up in red, but before, if I close it again, it's not even red, it's just white. So there was nothing here to tell us that there was a problem. And that's a bit of a problem because what's the point of having typescripts and type checking your code if you don't know about potential problems?

Turns out fixing that is really easy though. We'll go to the package.json, and in the package.json, you've got some scripts. And we'll add one or actually two more. So we're gonna call it compile. And it's not gonna do next build which would actually tell me about this error, but I'm gonna call the typescript compiler tsc. And strictly speaking that would be enough. But just to be clear, I'm gonna give it an additional option no emit. So I run the typescript compiler but do not emit any JavaScript based on the typescript you find. The no emit is actually automatically done because it's in the configuration already, I just like adding that to be clear. So if I save this, I see the compile script show up down here. And if I run that, it should point that type error out. And indeed it says here on in index.tsx on line 84 there is a type error, type number not assignable to string. So the very type error we just saw. So I can, if it opens up there it is. I can change this so I could say, well, that should actually be the string one and then the type error goes away. Now in my terminal window, the error stays and I could actually go in and run that compile again to make sure that the error is gone. But I kind of have to do that manually. So let me get this error back and let me show you what I often do, not always, but often. And I'll copy that compile, but I'll call this compile colon watch, and I'll give it an additional option of watch. Basically, when I run this script, it tells the Typescript compiler, go and type check all the code. Don't omit anything just like before, but keep on doing that anytime you detect a change. So keep watching the files and if you detect change to a file, so file being saved, automatically rerun those types again. So let me save that and start that script. So it says start compilation in watch mode. It comes up with the same error because I did my fix. Now, if I go back in here, I could say, well, that one should have been the string one. I save and immediately it runs and it detects there are no longer any errors. And the same would be true if I reintroduce an error, I save and it automatically warns me. In this case, I don't actually want that error, I want it fixed. So we'll fix it. Now, I did say I usually do that, not always. There is a slight problem with, or not problem, but a slight downside to doing this. I've got a pretty small project here and then it's perfectly fine. But if you've got a large projects, rerunning the TypeScript compiler on every file that's being saved is gonna cost quite a bit of resources. So all larger projects, I don't do it this way. I still have to compile script in here, but I will add a pre-commit hook. And in my pre-commit hook, so the Git pre-commit hook, I will run this compile step. So before I commit, I actually make sure that there are no type errors. And if there are, it aborts my commits and I fix them. So I can manually run and compile when I want to. I get it automatically when I commit and it's a little bit more performant. But for smaller projects, I typically use this watch option because it's fast enough and works really nice. So here's the change I made. Remember all of these slides are links. So if I click on that image, it takes me to the actual commits.

5. Converting JavaScript Component to TypeScript

Short description:

In this step, we convert a JavaScript component to TypeScript. We explore the combination of JavaScript and TypeScript in React components, the use of JS doc notations, and the behavior of TypeScript when encountering JavaScript code. We also examine the type checking capabilities of TypeScript and the importance of keeping code in TypeScript for better checking. Additionally, we discuss the file naming conventions for TypeScript and React components, highlighting the differences between.TS and.TSX extensions.

Here I see it's compiling the code and here I see the commands being added and the fix I did for turning that one into a string instead of a number. Of course it could have been different fixes here. They're not all that important. So the error we saw and the one we fixed. So let me open up the breakout rooms again. I'm going to do it a bit shorter because this is a pretty small change. So I'm going to open them for three minutes in total and go and add this compile command and a compile watch. Do yourself a favor, keep running the compile watch just like I did, because it's going to be useful throughout the whole workshop to see type errors appear. Because we will be making changes to say the tsconfig file, the TypeScript configuration file, and all of us will see compile time errors appear. So it's kind of useful to have that running. So let me open up the breakout rooms and I'll see you all back here in a few minutes.

So that's everyone back. So in the chat window, Philip asks, isn't it something like what was fork tschecker webpack plugin for Next.js? Yes, there is. That works perfectly fine. There is a very similar one for create react app. It's also, I think it's actually a fork of the same original package. The issue with those two though is they only check code, which is included in your webpack bundle. So if it's codes that you're not bundling, then it won't be checked. And that could include new codes you're writing. Other codes which you could potentially not include that way is unit tests, and unit tests exercise her code. But if they don't actually type check correctly, that could be a major issue. And Adam asked about the slides. So let me copy that, because one of the issues with Zoom is you can't see any messages from before you joined. And I see someone else beat me to it. Thank you. So let's continue with the next step. And in this step, we're gonna convert some components or one component that is from JavaScript to TypeScript and see some different options there and what the behavior is. So I'm gonna go to step one and I've got a little component here which says, Hello JavaScript. Not a very complex component. And the name or the message suggest it's written in JavaScript while the rest of the application is TypeScript. Now, the first thing to note here is that it's actually perfectly fine to combine JavaScript and TypeScript. TypeScript will do its best when it encounters JavaScript, looks at the code there, tries to infer what the types, the parameters, et cetera, to functions are. And in this case React components are just functions, so TypeScript will work with those. You can use JS doc notations to actually provide TypeScript quite a few hints about playing JavaScript code. So let's take a look at the actual code. You can close those. We'll go here, and we see that I'm doing imports of some alert components and I'm rendering pretty straightforward standard React here. Nothing strange, nothing funny. And as you can see, this is a TypeScript file. We're doing import, type, et cetera. So typical TypeScript things. If I go to this alert component, you can see here from the extension that it's actually a dot JS file, so it's JavaScript. Still pretty standard React components. Nothing at the words, and if I hover it, Visual Studio Code will kind of read this as regular code and tell me whatever it knows about it. So it knows there is a parameter message ID and a parameter variance of some kind of unknown type, so any, could be anything. It knows here about a div, which is an HTML, a JSX intrinsic elements dot div with a bunch of properties, and it actually knows about this. So if it changes to rule two, oh, actually, it's not gonna complain about that yet. It will later. If I go back to the page, which renders it, I can check and it actually tells me, okay, there is a message ID and there is a variant there. Can't really do much type checking about those. If I say we'll turn this into a number, it's gonna be perfectly happy because variant is of type any. Of course the runtime isn't gonna be happy. The colors are gone. Let me undo that. But we actually do get some validation here. If I remove these two props being passed in, I do get a compile error here saying that the type empty objects, so the props being passed in, does not match to acquired type, which is an object with a message ID and a variant. And it's missing both that message ID and the variants. And if I save this, we actually see that same error turn up when I compile. So it actually works, but still we prefer to keep things in TypeScript and have some more checking there. Now let's rename this file from JS to TS. And immediately we start getting a bunch of errors. It's complaining about the operator, the bracket hair, things like that. The reason is I'd renamed it the.TS TypeScript File. Which React, the original JavaScript practice was to have.JS files for regular JavaScript and.JSX files for React components. And when TypeScript started supporting React, I should say, it followed the same convention. So we have.TS and.TSX. Now in the JavaScript world, that distinction was dropped and nowadays pretty much everyone just uses.JS including React components. But for TypeScript, you still have to follow.convention..TS is for regular TypeScript, TSX is for TypeScript components. And the reason is, there are some slight differences like brackets, which have slight different meanings in the React world and in the standard TypeScript world. So I'm going to rename this file again, but now, under.TSX.

6. TypeScript Compilation and React Components

Short description:

The errors related to angle brackets and message ID type have been resolved by describing the expected types. The use of Bootstrap for styling is demonstrated, with GitHub Copilot providing a list of known variants. Runtime errors may occur when renaming files, but can be resolved by restarting the Dev server. TypeScript provides hover and autocompletion features, as well as immediate compile errors for invalid input. The importance of explicit returns in React components is highlighted, with the use of error functions and React.fc to define components. Display names can be set for better error stacks.

The errors here, about the angle brackets, et cetera, they're gone. All of that's fine. Now, we just have an error here saying, OK, the message ID is of type any, and it doesn't really like that. So we need to describe what they should look like. A couple of different ways you can do that. You can do it with an interface or with a type definition. Definition. They're almost the same. There are very few practical differences between the two. So I typically use type, but if you use interface that's perfectly fine. So I'm going to put message ID and the variance in here. So the message ID is going to be some kind of string, and the variance is going to be a list of specific value. So it's not going to be any string, but it's actually going to be a bunch of specific styles. So for the styling, I'm using Bootstrap, not because I think Bootstrap is the best and the way to go forward, but because it's simple. And I'm using GitHub Copilot, and GitHub Copilot is smart enough to understand, hey, there is Bootstrap here. There is a variant in Bootstrap. These are known variants. So it actually comes up with a nice list of the ones we can use. And now I can say that's props being passed in is of type props and my compile error goes away. I do have a runtime error, because I renamed my file. The Dev server is actually slightly confused, so I have to restart that. That's only because I renamed the file, not something that typically happens all that often. And now, if I go back here, everything works exactly the same way. But now I should actually change this a bit to say hello TSX and we get a new message, hello TypeScript. Now, if I hover over message ID, we see it's of type string. If I hover over variant, we see that it's also a string with a specific subset. And if I now type something invalid in here, we can see that I immediately get a compile error. So decent type checking. Let's set that back to primary. And actually what's also really nice. If I start typing, I get a message. I start typing, I get auto completion. So I know warning will work in there or say, there's a no, fatal wasn't, not error. I forgot what the exact values were, but we'll set it back to primary. So that's kind of nice, works. That's it's not what I typically do, although this works perfectly fine. Cause what could happen for instance, I might make an error in my components. I'll just remove the return statements. So I've got a function here which is supposed to be a React component and it takes some props and it actually defines some JSX, but never returns that. So it implicitly returns an undefined. Not getting any compile time errors here. This file is perfectly fine. I do get a compile time error here saying alerts. Well, it returns a void, so nothing, which basically means undefined, although it's an implicit undefined instead of an explicit one. So it's invalid, but I get the compile error not where the error is, I'm getting the compile error where I'm using it. So I really should go in here and be a bit more explicit. Now there are different ways you can do that. But the simplest is define something like that, JSX elements, which I actually never use, I typically use React's element, I guess. But that actually works out to the same thing. Now it complains here, I thought it was to resolve, but it's already resolved. Now it's complaints here saying, well, it doesn't actually return anything, so this function is not valid. So I need to put the return back in here, and now this component is happy, but now at least compile error happened in the location where the error was, instead of a location which just happened to use that. Now this works perfectly fine, but I still prefer to do things a bit simpler, because I kind of have to remember to put that in here, and what are the actual valid return types for a component? That's React element, but it could also be null, or actually more valid returns. So what I actually do is I use error functions, so I'll turn this into an error function. Const alert is this error function, and put a fat error there. So now I've got exactly the same thing, but defined as a fat error function. But instead of defining this, the return type and the type of props, I'm gonna say this constant alert is of type React.fc, which stands for functional components, and that takes a generic parameter, so between the angle brackets, saying the kind of props you want. Now, React.fc describes what the component looks like, so if I now were to remove to return, it will automatically complain saying that this alert doesn't match, or if I would do return undefined, which is actually valid nowadays in React 18, but used to be an invalid return type for a components, and React.fc still complains about it, it says undefined is not assignable as a valid result, but null for instance is a valid results, and it's perfectly happy with that, so if you conditionally want to render something, but not in all cases, then I could just return null, and it's perfectly valid, which is better than me typing in the actual result here and having to remember what's valid for React and what's not valid. So let's get rid of this null, and make sure it actually renders properly then. Now there are some drawbacks doing this, and one of them is when you start using generic components proper types, and we'll do that later, and then you'll see me convert and fat arrow function with React.fc like this to a named function where it's a little easier to work with generics. But in almost every case, this is how I define my React components. Another exception with next is if it's a page, next actually defines the next page which is kind of like an FC, a functional component, but specific to next pages knows a bit more. What's also nice about this, it knows about things like display name, et cetera. So I could do alert. It knows about things like display name default props, contact types. I can say display name is, and specify alert. So I get nicer component stacks at build time because the alert function is just a local function. When we create a minified build, it's gonna be minified to A or B or something like that. But this display name, if you set it, will be used in the call stack with an error. So, useful to set, and in this case, it's type checked.

7. React.fc and Mutually Exclusive Props

Short description:

In this section, we discuss the use of React.fc and the controversy surrounding implicit children. We also explore the use of React props withChildren and the importance of being explicit about adding children. The issue of generics with React.fc is mentioned, along with the alternative approach of defining children as a valid React node. We then move on to the topic of mutually exclusive props in components, using the example of a dual alert component. The potential confusion arising from having two props, message and message ID, is highlighted. The TypeScript behavior and possible rendering scenarios are discussed.

So if I forgot, well, what was it? Display name with a lowercase N, it actually complains here. And of course, if I say, if we could see that in the watch window as well, fix that error, and we're good to go then. So that's basically converting Java scripts to TypeScript for React components or writing new TypeScript React components. Here's the definition of the component I used, in this case, to list of variances slightly shorter, but that's just to make sure it fits on the screen. So, please go and do this. Just gonna open up the breakout rooms for four minutes again. That should be enough for this. I'll see you all. Actually, let me answer some questions first. Functional program with interface is better than type. Well, technically speaking, there isn't much difference between interface and type, but if you prefer interface, be my guest, it works perfectly fine. What did you fill into in the union or variant values? Yeah, that was actually auto-completed by GithubCopilot, but basically variant defined like this, like their message ID says it's of type string, so any string value is valid. Variants says it's this or that, so it's specific strings primary or secondary or success, another string won't be allowed, or you could actually even do say the string primary or true or seven, for instance, and then it's one of those known values will be acceptable but all the others are not. And indeed, I'm using GithubCopilot there which is very convenient, especially if you've practiced a presentation, so it already knows what kind of thing you would do. React with prop like ID, page ID, should we use number or string type? That really depends on your data type. React doesn't really care because a prop could be any type. It could be a string, a number, an object, a date, so whatever suits you. There are some places where it does matter if you're gonna use something in, as a React key for instance, a React key is defined as being a string or a number. So you'll have to use one of those. But if it's just your own prop, use whatever you do. So is it okay to use React.fc? There's some controversy over like implicit children. That's actually not the case anymore. So that used to be the case. So that's a question about children. Let me go back to the code for a sec. So if I include children here, you can see, I don't get any code completion. This is something which changed I think like six, seven months ago. In the type definitions for React.fc, they would take the props you passed in and include children to it. That's actually been changed and they removed children from it. They probably broke quite a bit of code with that, but that's probably for the best. So if you want children in here, you kind of have to be explicit about it and add those yourself. So there is still some controversy around that. I'm perfectly happy with it though. So, here it says the same, react.fc is discouraged. Usually your thoughts, well, I don't think there is an issue with it. The only issue that remains is with generics and I'll come back to that or as for instance, indeed, you can use react.props.withChildren and then you do get the children. Although to be honest, for some reason I typically don't do that. Not sure why, maybe I should, because that's what I typically do is this. I just go into my props and I say, children is a without L, react, node, which basically means any children, like that, children. So any valid React node, so it could be a React element, it could be just a string or something like that, which is valid inside of a child will be valid. And now I could go in here, say, and this is no longer self-closing element. I could pass something in here and that's perfectly fine. If I don't have this, now it's actually going to complain saying, well, there are children there and that's not allowed. So let's get rid of these again and make Typescript happy. No more questions, I think. So let me open up the breakout rooms so we can do this and I'll see you all back here in a few minutes. Okay. That's everyone back. Everyone converted to components and got that working. So let's continue. Someone has their mic unmuted. Let me actually change it, yeah. So, I'm going to look at another component which is slightly more complex and we're going to look at mutually exclusive props to components. Now, I'm doing this in the context of React Component but the same trick actually works fine with any type script. So if you're calling functions or having results from functions that would be perfectly fine to use exactly the same trick there. So let me show you first the code. I've got, first we'll go to the page, mutually exclusive. I've got a component here, dual alert. It takes two props, a message and a message ID. And it needs to render a message. Now the name of alerts already says it should render a message. Can I suggest that? So kind of like the alert we had in the previous example. And there it had a message here. There are two props, but it's like what's gonna happen. If I look at this, based on the name of the component, I'm kind of expecting it to render a message, not two messages. There's a message and a message ID, which of the two is it actually gonna render? Not sure. Is it gonna render both of them? Well, maybe. So let's take a look at what TypeScript thinks we can do with this. Let's make a few copies of this. And let's see, let's render it with just a message.

8. Enforcing Mutually Exclusive Props with TypeScript

Short description:

To ensure mutually exclusive props in a component, we can define the props using TypeScript's type system. By making one prop required and the other not, we can enforce the desired behavior. Additionally, we can use the 'never' type to prevent certain combinations of props. Although this approach may result in duplicated variants, we can simplify the definition by using brackets to group the mutually exclusive props. TypeScript's strong type checking helps catch invalid usages at compile time, providing better clarity and preventing runtime errors.

Apparently that's fine. Just a message ID. That's fine. Both was fine. And we'll remove both of those props. And I can save it. No compile error. So apparently this is fine. So let's go back to the browser and see for a moment what happens, and apparently this isn't fine. So let's just go back to that original example for a second with the two props and see what happens if I just render this with both a message and a message ID. What are we going to see? And by the way, this message ID is the same from the previous example, which renders hello TypeScript. But it doesn't actually render that. It just says, can we do mutually exclusive props? So apparently message is more important than message ID. Well, let's go and take a look at the implementation. I just hit F12 to go to the implementation and we can see that we've got message ID and also an optional variant, which I wasn't even using, where question marks here, which basically says, okay, they're optional. So they're of type string but you don't have to provide them, which means that message is a string or if not provided, an undefined. And then in the implementation, you can see that the actual message we're gonna render here, is either the message or if that's not passed in, because it's undefined, the null coalescence operator says, well, if message is null or undefined, then I'm going to take the right hand part of the expression. So it's gonna call the entl format message function with the message ID. So now I know what the actual implementation is. I can kind of make sense that if I'm not specifying anything, messages undefined, message ID is undefined, I'm gonna call formats message with an undefined ID, which is not valid. So let's actually go back here. So enable those two again. So we can see that those rendered just fine, but this bottom one doesn't. But in reality, what I want in this component, I wanna be able to do either of these top two, so these are good. Not that way. And here, these are bad, these are invalid, but instead of having to figure this out runtime where this will actually throw an error at runtime and this will kind of not throw an error, but make it unclear what happens. I want the compiler to tell me that and help me out. Turns out TypeScript is really good at solving these kinds of problems and I can make both of these invalid and both of these valid and more. So let's go to the components and take a look at the props definition. So right now I've said, both Message and MessageID are optional, but I actually wanna make one of these required and not both of them optional. And if I do that, then this should become invalid. So I could do something like say the Message is required, now the bottom dual alert is invalid because there is nothing there, but this one also becomes invalid because there is no Message, just the MessageID. I could make this one also required and now basically only this bad one is okay, so that doesn't quite work. But what I can do is I can say, copy this whole thing and say, I want a type which says I've got a Message and no MessageID and I've got MessageID and no Message. And they're both required and the variant in both cases is optional. That sort of describes what I'm wanting to. And here, it's like, this is fine, this is fine, this is invalid. This is still considered valid, which is not exactly what I wanted, but even worse, like over here, it starts complaining like, message does not exist, which is because it exists here, but in this type of props, it doesn't exist. So we actually want this message back in, but instead of a string, we want to say, well, that should never be passed in with this combination. And the same here. So, the never type will basically say, okay, this should never be allowed. So, we can do a message or string and no valid message ID, or we can do a message ID and no valid message. So now, both of these are invalid. In this case, because message is a string and message ID is also a string. But the two I want, with either a message or a message ID, also become invalid. The reason is these properties are required. So, I need to make one small tweak to it and I make these optional. So, message there optional, and message ID. So now I'm saying, I want the message of type string, an optional message ID never, which means that it's defined here as string or undefined, but it's never going to be passed in combination with a message. And in this case, it's the other way around. Now, if I go back to my component, I can see that the two intended behaviors are fine, no compile errors. But both the unintended usage with both message ID and message, or with none of them triggers a compile error, which is kind of nice. The only thing is I'm duplicating the variants. Not so nice. Yeah, well, if it's just a variant, that would be okay. But if that's a longer list, that would become kind of tedious. So I kind of want to get rid of that. Turns out that's quite easy to do. What I can do is I can put brackets around here saying I want either of those two and I want some more props and that's the variant. So now my prop definition is, it's either a message with no message ID or a message ID with no message, and we've got the optional variant. And of course that doesn't need to be optional. I can do whatever I want, but that's gonna work every time. So that way I've got a really nice way of typing my components or as I said, just general functions and everything will work. So common doze out because they don't compile and we're all good to go. And I can use my component in only the valid ways. So an example of the props I just did. And you see the compile error show up until the components are fixed. And then we fix the components and we're all good to go. Let's see what are any more questions. No, just linked to the repository but that wasn't where the answer is, so thank you for that.

9. Breakout Rooms and Optional Props

Short description:

The question mark makes the message or message ID optional, while the 'never' type indicates that no valid value can be passed. The question mark and 'never' work together in this case to define the optional nature of the prop and restrict the possible values. Let's continue.

So let me open up the breakout rooms. And just as a reminder, this was in section two is section two mutual exclusive and the actual codes was in the SSE folder in zero to mutually exclusive the jublealert.tsx file. So I'm going to open up breakout rooms again and I'll see you all back here in a few minutes.

So everyone's back. So there was one interesting question in this course. I find message question mark colon never less intuitive than message colon mark never. Any reason why it's this way? Well, the question mark makes the message or message ID was actually in the question, but optional. And if you don't specify that then you have to include that property which was the prop of course it being never passing any specific value in it is invalid. Like if you have a proper type, never you pass in a string it's going to say a string does not sign to never if you pass in the number, it's not assignable to never. So anything is not assignable to never. So you could pass something in and cost it to never but that's kind of, well, defeating the purpose. So the question mark there just says, okay, well it's optional and the never says and you come to provide any reasonable value. So they kind of work together in this case. Okay, let's continue.

10. Strict Settings in TypeScript

Short description:

TypeScript has strict settings that can be enabled in the TypeScript configuration file. It is important to set strict to true to enable stricter type checking. However, there are additional strict settings that can be enabled to make TypeScript even stricter, such as no unused locals or no unused parameters.

And the next feature I want to look at is strict features. Now first of all, TypeScript has strict setting and by defaults in TypeScript that's not enabled. Let me open the editor here. There's TS config here, the TypeScript configuration file. But if you use create next app like I did here to create the Next.js application or you use create react app or any of these things then they will all include strict issues while strict is true and set TypeScript in a stricter mode. Now I highly recommend you do not set that to false because TypeScript is a lot less strict and will let many errors just slide by. And so leave that set to strict. But the problem with it is a lot of people assumed that setting strict to true is kind of all it takes you set strict to true and now TypeScript is in strict modes and it's as good as it gets. But it turns out that's not the case. There is a whole list of strict settings and I think I've listed all of them but I'm not even a hundred percent sure of that which are not influenced by strict which will make TypeScript even stricter. And there are things like no unused locals or no unused parameters.

11. TypeScript Type Checking and Index Access

Short description:

If you have a parameter in a function that's not used in the function body, TypeScript will fail the type checking. No full true cases in switches can lead to errors. Setting no unchecked index access to true helps detect and fix errors. TypeScript can't catch runtime errors, so it's better to catch them during compilation. An example is given with a pizza ordering application where adding mushrooms causes the application to blow up. By enabling no unchecked index access, compile errors are generated, preventing runtime errors. TypeScript infers that accessing an out-of-bounds index returns undefined, and we can't call functions on undefined.

So with the last one, if you've got a parameter in the function, but it's not actually used in the function body, TypeScript will type fit or fail the type checking on that saying, well, you've got a parameter which you're not actually using. Some of these are somewhat opinionated like no full true cases in switches like a lot of people don't mind falling through in a switch statement from and having multiple cases do the same thing. It's a feature which can lead to some errors if you're not careful with it. But if you're careful, I guess it's fine.

The one though I would really recommend you enabled is this one, sets the no unchecked index access to true. By default it's false. Setting strict to true does not make this true. It remains false. And this is actually a cause of quite a few errors. First, let me show you what's gonna happen. Let's go back to index for a moment and I'm gonna create a new array. It's an array and I'll put in some numbers here, one comma two comma three. So I've got an array with three elements and then I could say const item is the value of data zero for instance. So item is never read but you can see that it's considered a number. Over here, TypeScript says const item is inferred as being a number, that's perfectly fine. So let's actually print this and see if it really is a number, of course. I'm sure everyone accepts that it is but let's make sure, where's the right browser window here. So if I refresh, we could see, it prints out one twice but the bottom one is actually the console.log of the item I just created. So, TypeScript thinks item is a number and indeed it is a number. But let's say, I'm asking for element 10, so the 11th element in the array of data. There were just three in there, so that's not going to work. It's not a compile error, and if I check item, it still thinks item is a number. Well, obviously it isn't. If I refresh and I check, it's now going to print undefined right here. So, that undefined is because we're not actually getting a number. We're outside of the array bounds, and that's not an error in JavaScript, so it's not an error in TypeScript, it just means that instead of an element, we get undefined. Yet TypeScript thinks it's a number. So, it's perfectly happy if I say to local string, for instance, on it. So, give me the value it should be. If I go back for a moment to say, element 1, so it should pick up this two and print outs two as a string, which it does, perfectly happy. But now if I go back to 10, I get a runtime error. But no compile errors, nothing. Well, this is exactly where the no unchecked index access is gonna help us detect these errors and help us fix them, prevent them, because we want to catch errors as soon as possible, keep the feedback cycle short. And I'd much rather have TypeScript tell me about a potential error so it can deal with it quickly than maybe it happens in some vague runtime situation where it might cause an error or it might not.

And it turns out, let me actually disable this. So I want the application running for a moment. It turns out that this error actually happens. If I go to three more straight, I've got a little pizza ordering application and I can order pizzas, pizza margarita, or I could say, add some olives to it. And all of that's perfectly fine. And I could say, give me a pizza, Alfunghi and that's fine. Add some cheese to it. No problem. Oh, like some mushrooms as well. And all of a sudden my application blows up. So my application works mostly fine. It's just a matter of when I do is very specific action in this case, selecting mushrooms as an additional option that my application blows up. Now I actually introduced this bug intentionally so I know it's that mushroom parts. All I need to do is add that mushroom. And it blows up any other ingredient is gonna be fine. You're building an application and you want to depend on testers to find that. Well, maybe they will, maybe they won't, but it's kind of like, well, you have to be very specific. It's only this very specific extra and any other action on this page is gonna be fine. So there is a pretty decent chance they won't even find it. And we'll only know about this when the application blows up in production, which is something we'd like to avoid. So let me put this one back in. So we know we've got the compile error here, or at least we know we've got a run-time error here but no compile error. And let's actually see if we can get to this into a compile error by changing that tsconfig option. So I'll add to no unchecked index access and I'll set that to true and I'll save this and immediately we see compile errors turn up. And the first one is in that index.ts where it says, this is no longer valid. And now if I look at what item is, it says, well, it's a number or undefined. Basically in this case, TypeScript could infer that there are only three elements so 10 is always going to be invalid. But it basically assumes, okay, we've got an array, we're getting some data from an array or some structure like that. It could be a record as well where we're using property names. And we're not gonna assume that we're getting valid item back, but we're gonna assume that it could be that item or an out of bounds thing, which is gonna return undefined. That's why item now resolves to number or undefined. And we can't call function on undefined. So, the way around that is now we have to check. Get rid of that, we don't need that. So I check if item.

12. Fixing Errors and Best Practices

Short description:

To fix the error, we can add the name and price to the object being returned. Another option is to log the error using an error collection service. Enabling 'no unchecked index access' and 'strict' in TypeScript is highly recommended. It is also good practice to use TypeScript, ESLint, and Prettier together for type checking, code quality, and formatting. TypeScript checks types, ESLint checks for bad practices, and Prettier handles formatting. They are complementary tools. Let's now proceed to the breakout rooms to add 'no unchecked index access' and take a 10-minute coffee break.

So I check if item. So if item is true feed, that means that it's not going to be undefined. I guess zero would also be falsy. So we still wouldn't print that. So maybe the type of check would be better. Type of item number like this, so even with zero it would work. And then inside here, it says, okay, well, if we get here, item really is a number and no longer undefined. So that fixes this, but how about that part where the application actually blew up? We now see additional error here. And if I go there, we could see there is this function, get extra ingredients. It takes the extra ingredient name and returns the definition of that, which if we look at it, is a name and a price. And if we recall the actual error here, let me order this again, we can see that it was trying to read a price from an undefined value. Well, that's exactly what the TypeScript compiler was telling me here. I'm not returning an extra ingredient here, I'm returning an extra ingredient or undefined. A couple of ways to fix this, the simple way which is also not the best, but for now, let's do that. I'm gonna put the name and the price in there, so I'm basically saying, get me the extra ingredients based on that name or if you can't find that, that's undefined. Just default to an object which has the correct shape. It has to have the correct shape. Like if I say the price, well, that's not even the price, but you see, I'll get an error because this object's doesn't match. And now the mid-year error is solved. Let me refresh this and go back and add that mushroom. Now it will just add the pizza and add the mushroom with an empty price because we don't know what it's supposed to cost, but at least the application doesn't blow up. This is kind of a quick fix. I typically don't do that for real. What I tend to do for real is something more like this. I will return that same object, but then over here sent message. Difficult word message to century or whatever error tracking service you want. We're getting into a situation which we were not assuming. We were assuming that we always had the extra ingredients listed here. So here I would log this to some error collection service I'm using. It might be Sentry, it might be something else. Doesn't really matter, but just to get a server-side log of the fact that I'm using an extra ingredient we didn't expect. So we can look at the dataset being used, fix the issue there, and at least prevent the application from blowing up. So let's do a console.log. There, how hard is it to type console.log? So now if I refresh, so we've got Clear Console, and where is that? Mushrooms. Here it is, I ordered. So we see that extra ingredient mushroom is not found. But the null coalescence operator can be a quick fix and plenty of places where that's actually perfectly appropriate. That's where I wanted to go, back to the slides. So I highly recommend you enable no unchecked index access along with strict and seriously consider the other options there as well. Cause they are pretty useful and there are a bunch of potential issues which this will catch but this is probably the most important from that list. And I will pretty much always enable this in a new project. Doing it much later is gonna be much harder because there will be many errors and you kind of have to address all of those. Of course, you can address a few and then disable it again and do it incrementally that way. So the option I added in the typescript.config file, the fix for the code, the simple version in this case and please go and do that. And there was a question. Is it good practice to put default values when creating the function? It depends a bit on the case. If you have good defaults and it's relatively simple then putting it in the function is fine. Otherwise you might not, there are also cases where you might certainly not wanna do this and say you're selling very expensive items and you're saying, well, if I don't know about it I'm just gonna give to the customer for free is probably not acceptable. So it really depends on the circumstances. In this case, having an X-ray ingredient for free instead of blowing the application is appropriate. In other cases, it's not gonna be and throwing an exception is gonna be better. But even then, I would explicitly throw an exception here instead of returning undefined and having the application blow up at some later point where it kind of says, well, I can't access price from undefined because then it's kind of like, well, why is this thing undefined? And you kind of have to track down the reason, then I would have an explicit throw in here saying, okay, if X-ray ingredient is undefined, throw an error, make it very clear why the application failed instead of some other point later in time. Another question, could you please comment on checking via TypeScript and or ESLint? They're actually complementary. I use both at the same time. TypeScript is type checking so it checks whether something which is defined as a string is a string, whether a shape of a person is really the shape of a person. That kind of thing. ESLint checks your code for bad practices. There is some overlap, like with the additional strict options, there was no unused parameters and no unused locals, that's typically something ESLint does as well. But the ESLint goes much further saying, well, they're naming things or like to react hook checks saying that you can't conditionally call a hook. Technically, that's perfectly to conditionally call a hook according to TypeScript that is not according to reacts but react it's not language. So ESLint rules work perfectly for that. So I use both. And in fact, I would throw prettier into the mixer as well, because I'm using prettier for my formatting, ESLint for my best practices and TypeScript for type safety. So any more questions? Nope, don't think so. So let's open up the breakout rooms and let's go and add these no unchecked index access. After that, I'm actually gonna take a short break. We'll do a 10 minutes coffee break. I'll start a timer on the screen so you can kind of see when we're all back. But it's 35 past now, so this exercise is gonna take like four minutes and another time 10 minutes after that. So we should be at around 10 to that we'll start with the next exercise.

13. Coffee Break and Data Validation

Short description:

We won't be able to cover what's left within an hour, but you have access to the slides, examples, and my contact information. I may turn this into a Udemy course and provide free coupons to workshop attendees. Using unions or enums for properties like variants depends on the runtime requirements. Enums provide better runtime support, especially for populating dropdowns. The next part will focus on validating data at the boundary. Type checking in TypeScript assumes that type definitions match the actual data, but mismatches can occur at runtime. An example will be demonstrated, showing unexpected behavior with price calculations. Despite no compile-time errors, the types do not match up, leading to incorrect results. Let's explore the validation of adjuncts data in the next example.

So I'll see you in a few minutes after this exercise. And remember, we'll take 10 for a coffee break then. Will we be able to cover what's left within an hour? That's a good question. I'm afraid not, but like I said in the beginning, I always create way too much material. There is always way too much I want to share. I always have more than I can cover. So no, we won't, but that said, you've got the slides, you've got examples, you've got my email and Twitter handle. So you can always do that afterwards. I'm actually considering turning this into a Udemy course as well. And if I do, I'll see if I can send all the people who registered for this workshop free coupon code for that course as well. Not a hundred percent sure if I can do that because I don't have all the emails, but the organization presumably does. Well, they must have, you registered. So I'll see if I can arrange that through them. But that is if I turn it into a course, I'm planning to, but that's also a bit time assuming. I see some people would like me to do that. So I see one question. Can you comment on the advantage, disadvantage of using unions for properties like variants versus using an enum? In many cases that doesn't really matter. The thing though is with an enum and especially if you make them string enums, you get good type checking. With numerical enums you don't get very good type checking. So avoid those. But string type checking will give you very good enums. But that's kind of like using unions in union type like I did with that variant. The difference comes at runtime. At runtime the union type for that variant is completely gone, there is just variant, nothing else. Now, if you define an enum in TypeScript, and unless it's a cons enum that's a kind of special case but a normal enum, it actually turns into an object. So at runtime you will have an object there and you will be able to go through the values of that object. And for instance, if you've got, say, you want to populate a drop-down and you've got, say VAT codes or country codes you want to list, then having an enum with the VAT codes or the country codes or any of those things is really useful because you can just loop through them and populate the drop-down with the possible values. And with the string, the union type, it's kind of like, well, the type checker checks that but there is no runtime thing to work against. So in those cases, enums are much better but if you don't need that kind of thing, if it's only for compile-time checking, it's kind of both perfectly good. So let me go to the next part and that's where we're going to do some validating of data at the boundary. Now this is, strictly speaking, not TypeScript. We'll join the TypeScript part to this later but this is because there is a possible mismatch between your types and what actually happens at runtime. Suppose I'm doing an AJAX request. Suppose I'm doing an AJAX request, I'm getting some data from a server. And then if I'm using TypeScript, I typically have a type definition of the things I'm getting back and then my code checks against those type definitions. But I've got no guarantee that I'm actually having a type definition which matches with the data being returned. There could be a mismatch there. And now TypeScript and all my type checking assumes that my type definitions are correct and will work against those. And you won't really know until run time that they don't match reality and that you're actually checking the wrong thing. And I've got an example of that. And let's go back to the browser here. Let's go to another example, validating data at the boundary. And it's kind of like the original example we had. I can add items to an order. I can add extras to it. But again, if I go and add that beach salfunghi with the extra mushrooms, then interesting things happen. Notice the price, like the prices are pretty reasonable, but all of a sudden the total is quite outrageous. And let me do that with just the peach salfunghi. If I add one it's 1,095. If I add one with mushrooms, now all of a sudden the mushrooms are just 60 cents. It should be just over 12 euros, but all of a sudden, sorry, over 21 euros. 21 euros, but all of a sudden I have to pay 21,900 euros and 60 cents. Not sure if that's a reasonable price at a pizzeria, but I'm pretty sure no one is just gonna pay that. So clearly, something is wrong here, but there are no compile time errors. We're doing type checking here. There are no errors, so apparently all my types match up. So let's go and take a look at those, Validating Adjuncts Data. I've got my types here. I'm saying I've got a pizza which is name and the price and some more stuff. And the price is number. I've got extra ingredients, again, with a name and a price of type string and number. So that all looks good. And suppose I change this to a string. Then immediately I see three compile errors show up. So that would certainly be a problem. But the way it was set up, no problem whatsoever. If I look at this error, it kind of suggests that something with addition of those numbers is going wrong. And if you're experienced with JavaScript, you'll all have seen the thing where you think you're adding numbers, but one was a string, which turns it into string concatenation. So if we kind of look at this ending six and the stuff here, smells to me like there's string concatenation going on. Well, in fact, it is, but can we detect that instead of just having these random wrong price and no errors, and for that matter, in my console, there are also no errors.

14. Validating Data with Zot Schema

Short description:

I'm using Zot to describe the pizza, or in this case, the extra ingredient, and specifying that the price is a number. Now with that definition, I can start validating the data. I create a Zot schema for the extra ingredient and a Zot schema for the records that contain the extra ingredients. I then load the data, validate it using the schema, and handle any errors that occur. This allows us to catch type errors at runtime and ensure that the data matches our expectations.

Well, we can. The thing which I like to use is Zot, but there are actually several different libraries you could do that, but Zot is really nice, and it will let you describe types. In this case, I'm using Zot to describe the pizza, or in this case, which actually is more important the extra ingredient, and specifying that's the price is number. That by itself doesn't buy us anything yet because that's just definition. But now with that definition, I can actually start validating it. Let's go back to the codes and let's add the schema.ts here, schemas.ts, and that should be a dot. And we'll import Zot. I was expecting type completion, but for some reason that didn't happen. We actually want this type, but now we want a Zot schema for that. So instead of a type, I'm going to create objects we're going to create an extra ingredient schema. And that's going to be Zot.object with that object definition. And that should consist of name property, which is a Zot.string. That should be a comma and a price, which should be a Zot.number. I'm doing the very basics here with Zot, but I could say, well, the name should be a string and it should is required. It can be an empty string. The price should be a number and it should not be negative. And it should be not more than say 100 euros or something like that. So I can put all kinds of restrictions there, but for now, I'm just gonna worry about the type being correct. So the other thing is the extra ingredients are actually stored in a records, which has a string type and then contains the extra ingredients. So I wanna make a Zot schema for that as well, because that's what's actually being returned. So we're gonna do the same with this. So we got the const extra ingredients schema is a Zot.Record and we wanna pass in the extra ingredients extra ingredient schema in there. So you can specify the key type, but by default, it's gonna assume that it's a string, which I can show like this. It says it's a records. The key is a Zot.String and the data is the part here, a Zot.Objects with name and the price. Now with this, I can go into the code that loads the data, which is the PizzaShopDataLoader components and in here it adds, actually loads the ingredients and now I can say, well, we're doing a fetch. Then we're turning that into JSON and then I want to validate this. So we've got some data and I want to call that extra ingredient schema and parse the data. So after I've loaded the data, I want to go and parse it and make sure that it's valid. If the data does not match my schema definition, this parse will actually show an error and tell us even where that error occurred. So now if I go back, I see, well, something went wrong. So an error was called. There was an invalid type. We expected to number, but we received a string and it actually tells us with the path where that happens. So in the mushrooms property, the price property was the wrong type. So now we know that, let's go and take a look at the actual data being returned. So, we'll refresh, so we'll see the AJAX request, see the extra ingredients JSON here. And if I scroll down right here, we can see that mushroom and indeed price is a string where the other prices here are numbers. So let's go and take a look at the data, the bad extra ingredients. Here we can see mushrooms being a string. I'll turn it into a number, save that. Now, my data loads fine, so no more errors and if I add a pizza Alfonghi and add some extra mushrooms, now we see that it behaves perfectly fine. The total amount is a perfectly reasonable 22.95, two and a half euros. This is something we can't catch at compile time because we're loading data at runtime through that JSON file. If we had that JSON file at compile time, we could actually infer the types based on the content of it, but at runtime, well, we don't know at compile time what's gonna be loaded. Presumably, this is gonna come from some kind of service which provides the data from a database, maybe from a JSON file, whatever, we can't really tell, so we can't really check at runtime. But now at least...

15. Validating Data and Resolving Duplication

Short description:

We can check the data at runtime to ensure it meets our expectations and validate it. We create schemas for the pizza and pizza array, and validate that we're receiving a pizza array. There is some duplication in the code that can be resolved by creating a TypeScript type based on the Zot Schema definition.

Sorry, we can't check at compile time, but now at least at runtime, we can check as soon as we can whether the data expects what we want and validate it. Now we can actually make this a bit shorter because this is perfectly fine, but because all it is is a function which takes some data, we can also just this way, pause in the function reference, which is gonna work out to exactly the same result. So perfectly fine now, no problem. But if I make this a string again, I need double quotes there. Now we have to same error again. Fix this.

Normally I will do exactly the same up here for the pizzas. Do the same thing, create a schema for the pizza, create a schema for a pizza array, and then validate here that we're actually getting a pizza array back. But because there's no error in there and in the interest of time, I'm actually gonna skip that. I'm just gonna do it here because that's where the issue was. But of course, that's because I'm lucky I intentionally introduced this issue so I know exactly what to do to fix it. In real life, that's not gonna be the case. So here are the schemas. Here's the pizza schema as well. In here you can see how you can create an array of something like with salt.array of some schema, or with some schema with salt.string.array. Either works. Both will turn it into an array of that type. Adding the parser, here's the extra ingredient I did and the pizza schema I would do as well. Now, this does leave a problem. And if I go back to the code for a bit, there is some duplication I don't like, like over here I've got the definition for an extra ingredient schema saying it has a name and a price, and in here I've got the same thing. There is a really nice way to fix that and we're actually gonna take a look at that a bit later where we can actually get rid of this type and say, well, we're gonna create a TypeScript type based on the Zot Schema definition, which is really nice and will save the duplication.

16. Handling Data Validation and Runtime Errors

Short description:

In production environments, handling data that does not pass validation depends on the use case. Default values or conversions can be used to handle invalid data. It is important to validate data on the backend as well to catch any invalid data before it is pushed to production. Different runtime type checking libraries, such as YUP, IOTS, and Zot, can be used for data validation. Each library has its own capabilities and features. SuperStruct is another library that provides a coerce function for data coercion. TypeScript can help in identifying errors at runtime, even when there are no compile-time errors. It infers types and provides type checking for configuration files and data. It is important to fully utilize TypeScript's type system to avoid runtime errors and improve code quality.

So a question is, I guess sort is only for local developments? No, that's not like with local development, it's fine, but this is very much a runtime issue. So even in, on your local development environment where you typically work with a small data set, your data might be perfectly fine, but that doesn't mean that your data is gonna be fine in production. So I would very much include this in my production code and do that same validation. Yes, it makes loading your data a bit slower, but compared to an Ajax request, which is gonna take like 100 milliseconds or so 10 extra milliseconds for validating data and having a sanity check, there is a pretty small price to pay.

So another question, what would be a good way to handle data which does not pass validation in production environments? That depends. There are cases where you could say, well I'll provide default values or I do conversions, like in this case, I could have detected that price was a string and you can actually set up sort in such a way that it will take a string and try to turn it into a number. And if it's a valid number, it will be perfectly happy and do that for you. Something that's commonly done with dates because dates don't share lies as a dates in JSON, but as typically as a string. So do they tell parse to get the actual dates? So that could be a way. There are other cases when that won't work because it's just something which is completely wrong and you basically have to failed the application. Now, I'm hoping that the backends is also validating the data. So you can have know on the backends already that it's invalid and things get fixed there before it's pushed to production, but you never know for certain. Things I typically see going wrong there is like on the frontend, you assume that's say the price is a number, but in the database, it's actually a number or no. And in some records, it turns out that the price is null, so you get the mismatch there and you can get runtime errors or so. So you typically want to know that and it's quite likely you won't know that in the development environment or even the staging environment before you release. So how to handle that in a case like that? Maybe providing a default of zero, maybe not. Depends on the use case, depends on what it is. If you're selling Porsches, then saying well, if I don't know price, I'll just give it away to the customer is probably not the best of options.

Another question from Suzanne, did you try other runtime type checking libraries? What do you think of IOTS for example? Yes, I did. Before I used Zot, I actually used YUP quite often. It has similar capabilities, but Zot is a lot better. The problem, or well, not really a problem with YUP was that it was written in TypeScript and the type definitions were added to it and type inferencing was added to it to do similar things you can do, although not to the same level as Zot. IOTS is pretty good. There are other ones out there as well. Let me think of some names. No, I can't think of any other names, but there are more out there. I would say IOTS and Zot are the best, and my go-to is Zot because I know it better, but...

Another question from Philip, I have a very good experience with SuperStructs, especially because it has a coerce function. Zot actually does that as well, coercing data if you wanted to. It's just a matter of how you set up your schemas. I've not even heard of SuperStruct, so I can't comment on that, but it's an interesting one I'll take a look at. So no more questions in that case. Let me open up to breakout rooms and let's do this exercise, and I'll see you all back here in a few minutes. Okay, that's everyone back. So let's go and do the next exercise. So it's quite amazing how many errors you can have even when using TypeScripts if you're not fully utilizing TypeScripts. And before I go into the slides, let's actually go to the page. Inferring TypeScript types. And it's another one of those cases where we had zero compile time errors, but an immediate runtime error saying there was something wrong with this type. Well, how can TypeScript help us in lots of cases not adding types ourself but letting TypeScript in for things will let TypeScript do a lot of magic. In this case, there is a configuration object which has a bunch of settings in there and we're loading some settings from a browser from it and rendering them. But basically because things were a little hard I added lousy type information to TypeScript which basically told TypeScript, well, I don't really know how to deal with this. Just ignore it and you end up with errors like that. So what does that look like? Let me close these windows. Where is it? Inferring types, nope. Down here, there's this configuration file. Basically I've got a configuration file here. In order to save, solve or compile error I basically specified any here and any is kind of the way in TypeScript will you say, well, I don't know, anything goes just treat it as whatever and we'll sort it out at run time. And as we just saw, sorting it out at run time doesn't work, or at least not always. Here I'm using the function which used that config object and I'm saying get me some section and get me a property from it. And if I look at the definition it will give me from some section, which is any string some item, which is any string and it returns something, any. I didn't really define the result here but it just kind of inferred that. Turns out I'm trying the section employer here good user and address above and below it. And in that config, there is a user and an address but there is no actual employer. So when I'm checking for this is the line that actually blows up. But even if I comment that out my code is gonna run again at least if I refresh the page it runs, but there are still errors here. This birth date is sort of okay. It works but in a way which React doesn't really like but I can't tell because there are still no errors here, nothing. Well, why did I put this any here? Let's remove it and see. If I remove it, it kind of starts complaining here. It doesn't know about section. So section is a string. It's understands what Conflict looks like now because it infers the shape. It kind of says, well, you can't just put any string in there that could be user or address. Now I could get rid of this and say, this could be user or address. And now I've just made the compile error a slightly different. It says, okay, well, that part is fine, but now it doesn't know about item. So item could be first name, birthdate, streets, house number, city, et cetera. I could do the same here. And it will basically be happy.

17. TypeScript Type Inference and Object Types

Short description:

And right now, it would actually complain here about employer, but then it would still let me ask for the birth date of the address, because that's a valid combination of section and item, which of course doesn't make sense. So doing this is not exactly ideal. So, what is a better way to do it? Well, it turns out TypeScript is really good at inferring things, let me put string in there for a moment, lowercase s, that it's valid again. And if I hover over config, you see that TypeScript actually figures out that config has that specific shape. It knows about first name, knows about birth date on the user, it knows about the address with the street house number. It knows the types of string and date, et cetera, so it's pretty good with that. Well, there is a TypeScript construct we can use to turn that into a type. So, if I define type, I can say we've got a config and instead of listing out what that looks like, I can say go and use typeof, and then that config object. So, the type config now, complains that it's declared but never used, is actually that same thing which it inferred of. So, I could've listed this completely, but now it infers it based on the type. And then if I make a change here, I say I add something else like that, and it needs a comma. I go to the config type and I zoom down and indeed, X is now of type string. Let's get rid of that.

And right now, it would actually complain here about employer, but then it would still let me ask for the birth date of the address, because that's a valid combination of section and item, which of course doesn't make sense. So doing this is not exactly ideal. So, what is a better way to do it? Well, it turns out TypeScript is really good at inferring things, let me put string in there for a moment, lowercase s, that it's valid again. And if I hover over config, you see that TypeScript actually figures out that config has that specific shape. It knows about first name, knows about birth date on the user, it knows about the address with the street house number. It knows the types of string and date, et cetera, so it's pretty good with that. Well, there is a TypeScript construct we can use to turn that into a type. So, if I define type, I can say we've got a config and instead of listing out what that looks like, I can say go and use typeof, and then that config object. So, the type config now, complains that it's declared but never used, is actually that same thing which it inferred of. So, I could've listed this completely, but now it infers it based on the type. And then if I make a change here, I say I add something else like that, and it needs a comma. I go to the config type and I zoom down and indeed, X is now of type string. Let's get rid of that.

18. TypeScript Generics and Type Safety

Short description:

We can use generics in TypeScript to tie the section and item together and ensure they match up. TypeScript is able to provide detailed type checking and catch potential errors, even if they don't cause runtime issues. By using the 'as const' syntax, we can make properties more strict and specify exact values. This is useful for configurations where we want to ensure specific values are used. TypeScript's type inference and generics make it easier to create robust and type-safe code.

So, if we know that much, we can kind of say, well, the section should be a property of config. Well, how can we do that? We can kind of take this, and instead of string say, we want this, instead of the type itself, we want the known keys. So with key of, we can do that. So now if I re-enable this line again, it actually complains saying, well, it can't be employer because it's inferred that it must be user or address. But if I add something else here, say, some objects, like this, and I need another comma there. Now here is gonna say, well, the section can be user, AAA, or address. So again, it is completely dynamic based on the object being used. Let's get rid of this because we don't really need that. Can't use that anywhere, anyway.

Now we want to do the same here. Make sure that the item is valid, but in combination with this, so I could pass a userFirstName, but I cannot pass in addressFirstName or addressBirthDate, that should be invalid. And of course, this should compile perfectly fine. So we want to kind of do the same here, the same as this, over here, but we can't just do the same because we kind of need to tie these together. And the way you do that in TypeScript is by using generics. So you specify those on the function level and we need to give it some name. And quite often you'll see the name t defined there, which is actually a lousy name, but that's just an historical thing. And in this case, we wanna say restrict that to that list of property or the key names of config. And with generics, we do that with extends. Now I'm gonna change t right away because like I said, that's a terrible name and t actually originally stands for type because in generics quite often the single type was used, so it's kind of like generic type. But now I'm gonna say this is the type for the section, so the generic section and then you can actually get rid of the t because it doesn't add much, so we'll add that there. And now I can reuse that in another point. So I could say, well, we've got this section and the item should be a property of section of the t section with the section property. No, sorry. Almost. This doesn't quite work. We have to do it up front here again. So we'll create a t item. Extents. I'll do key of type of and here we can use the t section. And I specified t item there. So now the section is going to be, t section is going to be user address and based on whatever section you've already put in the first parameter, the t item is going to be either first name or birthday or street house number city. No more compile errors here because TypeScript knows that this always matches up. And now we get compile errors here. So still have this one because employer doesn't exist. So let's get rid of that. But now it also complains here saying, well, we're on the user and there is no last name there. Or if I would say, user.street. There is no street there. There is a street on the address, but not on the user. So let's make this last name and say, well, if we add a last name. John Doe, now this compiles fine, and now this seems fine. At least it runs and no issues there. But I said, seems fine for a reason. If you might've noticed, there is still a red line here and here. So there is still something wrong. Turns out this enabled input says there is something wrong with the value. So the value for an inputs is defined as string number or string array, readonly string array, or because it's optional, it can be undefined. And birthday is of type date. Because now this is fully type checked, and here we actually get the date back, and here we get back that last name is a string. Or house number is a number. So now I could say, well, that shouldn't be a string. So we could do say to local date string for instance, turn this into a nice readable string for the birthdate and we're all good. So that wasn't even a simple compile error that was kind of like just cascaded down. And now TypeScript is able to give us all the required information and even provide us with potential errors there which didn't cause a runtime error but still made React a little unhappy. So, back to the slides. In the meantime, there was a question. Is there a way to make it more strict, for example, that first name can only be John rather than a string? Yes, you can. Let me go back here. What you can do is you can specify as const in here. So now, if I look at the config type, you can see that the first name is not just a string but has to be John as well. And I can do that at a property level. I can do that at an object level, so I could specify that here. So if I look at config, I see that user is now John Doe and the birthdate is still a date because that's an object. But the strings are immutable and the house number, string, et cetera, they're still strings because they're not. And I could even move that to root level. And now all of this becomes known. So we've got first name, John Doe, fixed. We've got the street address, city, et cetera, all fixed. This is really nice if this is really a config thing where you want to be exact of things, but in this case, not needed, but quite useful to know. Exactly what to type a team provider.

19. Using Generics in Functions and React Components

Short description:

Using generics in functions and React components allows for powerful type checking and alignment of data types. By using generics, we can properly type the section and item in the getConfigItem function, ensuring the results are also properly typed. This feature saves time and improves code quality. Let's now move on to using generics in React components to solve a problem with data alignment and type checking.

Yes, that's exactly where I would use that as const. I've got a team which margins pathings, et cetera, set up as tokens. And that way I know that something is not an array of numbers, which is by the way, I didn't show that, but let me undo this, but that's pretty nice. If I say I want to show something, if I say, data is an array and I specify one comma, comma two, for instance, comma three and I check it today that you can see data really is only the numbers one, two, and three. So not just an array of any number, it's very explicit about what's possible there. And without const, then you just get an array of number. So very useful.

Okay, let's go and do this exercise. So change the getConfigItem function and use the generic type to properly type the section and the item, which will automatically properly type the results as well, which is really nice. And a really powerful typescript feature which will save you lots of times and we'll actually do some more similar things like this in a moment with React components. But first, let's go and do this exercise. So we'll now open up the breakout rooms and I'll see you all back here in a few minutes.

Okay, that's everyone back, so let's go to the next exercise. So using generics in a function like that is really neat and really powerful, but it turns out you can do exactly the same with React components. The setup is slightly more work, but it's a very nice and powerful feature and let's actually go and explore how that works. But before I do, I first want to show you the problem we're going to solve again. So I've got two forms here. I've basically got a component which will create a little form based on the properties of an object passed in, we'll basically loop through that and create inputs for that. Have a little submit button and then provide or let the, or at least let the developer, I should say, provide both to object to use and whatever should happen at the submit. It's like I've got first name, last name here and the submit, it shows me the first name and last name. So the data is there, but for some reason when I'm doing something with it, but I'm getting John and undefined. Then of course, as we know I've got no compile errors. Kind of get the same here, working with an address. So everything looks good. I've got a street house number city. But when I render that as a string, I get undefined main street undefined new York instead of 123. And we don't need last pass here. So how can we go and fix this? The form being rendered is this. It's rendering a generic form component, passing in some values, first name, last name, John Doe, and then on the submits, it's rendering or calling into that alert function where we just saw the pop-up, and it's basically using values, first name, last name. But if I hover over, you can see the values is of type any. And last name here is with lower case n and up here with an uppercase N. So looks similar, but there is a difference in case, which is why this renders as undefined. And the same here, I've got the same form again, passing in values with street, house number, city, but house numbers with a capital N, and here I'm using it with the lower case N, but again, values is type any so no compile errors, nothing. And I really want the values being provided to my submit function, to line up with whatever I passed into that initial values group. If I go to the generic form component, it's kind of a simple react components. It takes a props, which is an initial values of any, and on submit, a header, and it basically loops over the items and renders those. Nothing very spectacular, pretty simple. But basically I want to get rid of these two anys and align those. Well, we could do the same kind of trick we did before, use generic. So I'm gonna say, this is of type T-Data. And then, here, I'm gonna say this should be a T-Data and this should be the same. So now in this structure, I've kind of lined these up that the initial values and the values passed to the on submit are the same. But now props is a dependency on a generic type, and in here that needs to be declared. So I could go in here and say, well, that should be an object of some sort or maybe something else. And actually that needs one more bracket to be correct. And actually I get a compile error here but we'll ignore that for a moment. The thing though, is like I'm specifying a type here but I don't really know the type here. Overhere, I know the type where I'm saying, well, this is the type which I should use. And in the case of the user, this is type I should use. I can't really specify that in here because I don't know, it depends on how this component is used. And that's where react.fc actually falls short. So in this case, I want to get rid of react.fc. Turn this into a normal component. So we'll do that with a quick refactor. Or sorry, normal function, I should say. And now say, this should be type props. Some type of value. Well, this needs to know ddata, where does ddata come from? Now, because I've got a regular function, I can specify that over here, which I couldn't with the generics. So now if I go back here, I can see I get a compile time here saying, well, last name with a lowercase n doesn't exist. Did you mean last name with a capital M? Yes, I did, and it's perfectly happy. And down here with house number, I get exactly the same. The old JSON's number to a capital N. And it's perfectly happy. So values is the correct type. Suppose I add postal codes to it. Postal codes like that. Now in the values for the on submit, we also have that postal code. So these two types are completely aligned. So looks good, except we still have compile error here. In generic form, we still have a property problem saying that values isn't exactly what it likes.

20. Using Generics in TypeScript and React Components

Short description:

We can use generics in TypeScript to tie the section and item together and ensure they match up. TypeScript is able to provide detailed type checking and catch potential errors, even if they don't cause runtime issues. By using the 'as const' syntax, we can make properties more strict and specify exact values. This is useful for configurations where we want to ensure specific values are used. TypeScript's type inference and generics make it easier to create robust and type-safe code. Another question, instead of copying the types from the value property from label input, can you get that directly using component props? Yes, you could. Indeed, that would work perfectly fine. Let me show you what Philip suggested. That's not where I want to click, like over here. Let me add missing import. So what did I do wrong? This should work. No. Not sure. But basically what I'm here, what we're doing is getting the type of labeled input. Getting the type of the value from there and setting that as the generic type which is allowed for the record. That should basically work. Something wrong and I can't undo my changes anymore, which is a little bit weird. For some reason, Visual Studio Code didn't like this and completely froze up. So let me undo that.

So here it says values won't work because object.keys expects the object being passed in to be an object. So we could go in and do the same as we did before. Where's the definition here. Over here say TData is some generic but there is a restriction to it. So we'll use the extents. And we just saw that an object was required. So we could say extents some object. And now this is perfectly fine, but over here, it's still a happy. It says, well, we can't use an object and then use a string accessor. So this isn't quite gonna fly. It's close, but not good enough. So instead, we'll say, this is not gonna be any object, but it's gonna be a record, which takes string as a key. And we really don't know what data is gonna be here. So we'll take any as the data type. It needs one more angle bracket. Now, the value is happy because a record actually extends an object. So it knows that that's good. Indexing it is fine because it knows the key is a string. And whatever values is, must have key strings. Over here we're happy. The only thing I'm personally not 100% happy with is now I'm introducing any. And any is kinda saying, well, this could be anything I don't care. But in general in TypeScript, if you don't care, a known, I don't know what it is, is slightly better. This case, that doesn't work though, because the value for an input is of type string or read-only string or something like that. So it's a known doesn't match that. So I could say, this is of type string, but then I could only work with string parameters. So in this case, it's kind of like either put in any, or what's actually better is saying, well, this could be string number, read-only string, or undefined, so we could put that in there. So now this matches up. So the value here is fine. The uses matches up, and the uses is actually slightly better because if I set If date is new date, something like that, and of course, that needs to be colon. It actually complains here saying, well, you can't because the properties need to be valid for an input. So it needs to be string number or a string array. So I could do this. Not too look old. Where's datestring? Because it's a birthday so we only need the date part. And now it's happy again. I would expect it to render now, but there it is. So it's renders the date. So a similar way of using generics but in the context of React and actually pretty powerful in a way where you can take something on a component level, pass it on to the props. And the props make sure that two different properties or more than two different properties aligned with each other and always work. And the cool thing when I'm using it, I did not have to specify it. If you want to, you can, I could add angle brackets here and say there is some specific type. So I might say create a property so I might say, this is an object, so I'll do it inline with say, age is a number like that. And now it actually says, well, okay, the initial values don't match up with it, so none of this actually compiles. Or I could say first name is a string, then that's valid. But it's complains about the last name. But the cool thing if you just omit this, it will infer it based on the properties, it will just make sure that these two align. Based on this object, it will infer that this all matches and works well, which is really cool. I really like using this with some generic components like this. It's not for all components but it's quite healing. Question, can this be done with an arrow function? Yes, it can. You can do this with an arrow function, but not with React FC. With an arrow function, the syntax is a little squeak here, let me actually. I'm not going to do that in the interest of time, but I would highly recommend turning it to a regular function because it's a lot more readable. Back to the slides. Basically, the stuff I just said, but in a shorter version, and the codes I just read and the fix. Another question, instead of copying the types from the value property from label input, can you get that directly using component props? Yes, you could. Indeed, that would work perfectly fine. Let me show you what Philip suggested. That's not where I want to click, like over here. Let me add missing import. So what did I do wrong? This should work. No. Not sure. But basically what I'm here, what we're doing is getting the type of labeled input. Getting the type of the value from there and setting that as the generic type which is allowed for the record. That should basically work. Something wrong and I can't undo my changes anymore, which is a little bit weird. For some reason, Visual Studio Code didn't like this and completely froze up. So let me undo that.

21. Using Component Props and Resolving Duplication

Short description:

Filip suggested using component props to get the props definition of a component, eliminating the need to copy the type. However, the usage of component props with or without ref depends on the specific component. The duplication issue with Zolt schemas and types was addressed by using the same type at compile time and runtime. BigInt is a valid type in TypeScript, and Zot, IOTS, and YUP are powerful validation libraries. Although there is no way to generate types for all exported schemas in a file, nested schemas can be inferred. The workshop was not completed, but participants were provided with slides and sample code. The possibility of turning the workshop into a Udemy course with free coupons for attendees was mentioned. The workshop concluded with appreciation for TypeScript and React as a powerful combination.

Well actually, well hopefully we'll come to something like that in another example. So let's go and do this. We'll see some more type inferencing after this, which is going to be the last exercise we actually get to. But there is an example of doing stuff like this in the slides and the code, so you can see that. So I'll see you all in a few minutes.

Okay, that's everyone back. So I actually just did, so the suggestion Filip came up with, he said well, using component props, you can get the props definition of a component which is actually one of the other quite useful, and using the square bracket notation you can get specific prop from that. So I'm saying, give me the type of the value prop on the props of the label input component, and if I check value is in each string number read only string array or undefined. And then I can use that so I don't actually have to go and copy that type which is indeed a better approach to just copying it or using any. So let's go and do the last exercise. Actually I think I'm about halfway through my workshop, but we're basically out of time. But that's fine. As far as I'm concerned, I'd rather answer questions and help you guys than finishing a whole bunch of slides and rushing through them. You can always go through them yourself. In the meantime, there was another question. Does ComponentProps use ComponentProps with ref or without ref? By default, if I'm not mistaken, it is without ref. But let's check. If you go to the definition, it actually tells you what it is. It's actually not using either of them because this is Component with ref and this is without ref. And this is actually different than either of them. So not 100% sure. I think this doesn't give you refs back. So I was assuming it was ComponentProps without ref, but it really doesn't. Interesting. Interesting. This is the ComponentProps is the one I typically use, but not always. Anyway, that was actually part of the next exercise so I'm going to skip that because we kind of covered that. What I'm gonna do is I'm gonna go into the one after that where I'm gonna solve the duplication I mentioned before with Zot Schemas and types because if you recall, let me go here, close all of these. We have these TypeScript type definitions for pizza and extra ingredients and we had schema definitions for pizza and extra ingredients and extra ingredients records, but they are kind of the same. I closed the other. So I kind of want one definition of what a pizza looks like. And now I've got one here, which is used for compile time. And I've got one here, which is used in runtime. And they really should always be the same. And I don't want to start copying them. Like if I think price should be a big int then I have to go and change it here and I have to go and change it here. Well, the good thing is, if I take this pizza schema and import salt and that pizza schema from here, salt has an infer not XML document an XML document and there you can specify the type of the pizza schema and that type of that should be and that complains a bit because it doesn't really like this but if I specify that this returns a type pizza 2 because we already have pizza then this should work but I still need to and what's wrong here that should be angle brackets like this. So now if I check out the definition pizza 2 you can see its name ingredients price extras exactly the same and if I go in here and say well this is not a number but a big int for instance I go back here and check the type of pizza 2 which is inferred and indeed here it says price is turned into a big int. I've got a type definition based on my Zolt schema. So let's turn that back into a number so now instead of having this definition I can say exports and then of course as pizza that type. And I can do the same with this extra ingredient. Well that's uh that was the record so that's an infer of this type not pizza schema extra ingredient schema and I can do the same here not this extra ingredient schema resolve to import and now all of these will match up with whatever is defined in Zotso using the same type at compile time as at runtime for validating the data and of course if I would change this to number sorry it was a number to string all I need to do is save and I'll start getting all sorts of compile errors saying that types are not aligned. I'm assigning a string to a number so I could undo that fix things so that's pretty powerful so question, is BigInt a type in TypeScript Yes it is, actually it's a type in JavaScript and TypeScript is a super set of ECMAScript so if it's a valid type in ECMAScript then it's also gonna be a valid type in TypeScript of course in ECMAScript you don't specify something as being of type BigInt but in TypeScript you could do that if you want to Not sure what the type is, I think it was an m one m, like this, no it's not an m what was it... Well I forget what the constant is, but there is a constant which would turn this into a big Int, instead of a regular number I need to look up what the definition is, and that is how you create a big Int in JavaScript as well So, really nice way with Zot IOTS can do exactly the same thing, works really well and YUP can do the same kind of thing as well, so different validational libraries can do that So the example I just did So another question, and I see an underscore underscore is not a way to change something into a big Int if that is what you mean you can use that with numbers as thousands of separators to make them more readable and TypeScript just ignores it. Does it have all the inferred types like in the command in the case you have like 50 types in a file don't think you can do that. I think you have to create a type per schema, but it will infer a completely nested schema So in the case here I've got an extra ingredient schema which is a record. I can actually infer that, and then based on that, I could infer its children etc. In the same way I did in the last example where I used the indexer notation on a type But if you... There is no way, at least not that I know of, where I can say give me types for all the schemas exported from this file It would be nice. Would be cool if we can. So we're over time. Feel free to do the exercise, but given that we're already five minutes over time, I'm gonna call it a day Sorry we couldn't get to the end, but like I said at the beginning I always add way too much stuff in there because I'm way too excited about all the stuff I want to show and I knew in advance I was not going to be able to show it Let's check how many slides I've got. We were at 46. I've got 78 slides So we were a little over half way. Still not too bad But you've got the slides. You've got all the sample code so feel free to play around with it You've got my email address and my twitter handle so feel free to contact me about it if you've got questions. In the meantime I'm going to check about turning this into a Udemy course and once I've done that I will contact the organizers and see if everyone who registered for this workshop can get a coupon code to get the course for free. No promises when that will be done or even if I do because I'm kind of busy. But I'm going to try to do it and when I do I'll try my best to get all of you a free coupon code So if there are any questions feel free to ask them now Feel free to unmute your mic if you want to, otherwise I hope this was useful and I wish you all the best with TypeScript and React Really sweet combination, really powerful and really useful Thanks Maurice, the part on the generics and components was really interesting Good to hear that, thank you You're welcome The good thing is that you know when to use generics, such and such, but actually writing them is... It can be pretty fun Well, it is a bit intimidating to get started with but once you know the basics of doing this, it is actually quite simple. Let's see if I can quickly find it in the slides. I have got some examples here which were going to do but I never got to. Deep read only here. There is a read only type mapping but creating a nested one, because the standard one is nested is really simple. It's just these few lines of code. Stuff like that, like the really complicated ones you can do, but they're really simple ones, which are quite useful and quite powerful. There are actually some really nice TypeScript utility libraries which provide things like that. That is, hold on, thanks. If there are no more questions I'm going to shut down the Zoom meeting. Thank you once again.

Watch more workshops on topic

React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Workshop Free
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?
There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.
Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.
You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.

React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Workshop Free
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.
The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.
React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.

React Summit 2023React Summit 2023
171 min
React Performance Debugging Masterclass
Workshop Free
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Workshop Free
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.

React Summit 2023React Summit 2023
152 min
Designing Effective Tests With React Testing Library
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents
- The different kinds of React application tests, and where component tests fit in
- A mental model for thinking about the inputs and outputs of the components you test
- Options for selecting DOM elements to verify and interact with them
- The value of mocks and why they shouldn’t be avoided
- The challenges with asynchrony in RTL tests and how to handle them
- Familiarity with building applications with React
- Basic experience writing automated tests with Jest or another unit testing framework
- You do not need any experience with React Testing Library
- Machine setup: Node LTS, Yarn

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.
In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
React Advanced Conference 2021React Advanced Conference 2021
27 min
(Easier) Interactive Data Visualization in React
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick
easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data
use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time

React Summit 2023React Summit 2023
24 min
React Concurrency, Explained
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar