Solution
Migrate to TypeScript
GreatFrontEnd has better questions!
We don't create questions because GreatFrontEnd already has the best possible library of questions and we recommend the same to our community.
Though we are a huge fan of text-based learning, sometimes we need to go for a video for a better experience. This is one of those times. We still tried to give you as much as possible in text.
Watch the video for a better experience.
We will only discuss the TypeScript part of the solution. The other logical solution is already covered in the JS version of the question.
The way we like to approach migration to TypeScript in React is to handle the child components first and then move up to the parent component.
In our case, the component tree looks something like this:
So, we can work on the AddTask
or Task
component first.
If you feel like something is not working as expected with TypeScript, just try to restart the TypeScript server.
This is how you do it in VSCode:
-
Open the Command Palette:
- For Mac: Press Cmd + Shift + P
- For Windows: Press Ctrl + Shift + P
-
Type "TypeScript: Restart TS Server"
-
Select the command from the list
This will restart the TypeScript server, which can often resolve unexpected behavior or issues with TypeScript in VSCode.
Sometimes a restart to the dev server might also be needed.
Watch the video for a better experience.
Migrating Task component
First, rename the Task.jsx
file to Task.tsx
.
Your code editor should light up with red lines. Especially at the part where props are destructured.
Ok, so how do we define the props?
We can either use a type
or interface
(type is the preferred way for props at least but both will work the same in most cases):
So, we said to the Task
component that it can take three props: task
, idx
, and onDelete
using our TaskProps
type. For now, we said that all these types can be anything (any, not a good idea).
At least, the red lines are gone from the Task
component. But our TypeScript config tells us to not use any
types. Of course, because using any
for the types defeats the purpose of TypeScript. Let us narrow down the type of props to the ones we need.
The task
property from the use of it in the component we can see that it is an object
with the property taskName
. So, we can narrow it down to something like this:
What about the idx
property? It is a number. So, we can directly narrow it down to number
:
What about the onDelete
property? It is a function that takes no arguments and returns nothing (void is the type for nothing in TypeScript). So, we can directly narrow it down to () => void
:
Perfect, no more red lines. Now, we can work on the AddTask
component.
Migrating AddTask component
Same as the Task
component, we can rename the AddTask.jsx
file to AddTask.tsx
.
This time, we see more red lines. One of them is at the usual props destructuring while the other is at the handleSubmit
function definition.
Let us use any
and make these red lines disappear for now and let TypeScript warn us about the any
type.
Just like the onDelete
property in the Task
component, we can narrow down the type of onAdd
to something like this:
This time we had to specify the parameter types because we are passing the task
to the onAdd
function.
Let us work on the handleSubmit
function now. Let us check where are we passing this function in the markup.
Hmm, hover over the onSubmit
attribute and see what the editor is suggesting the type to be.
The suggestion is it could be of type React.FormEventHandler<HTMLFormElement>
. Let us use this suggestion and see what happens.
You will see more errors in the console, to be specific the following errors:
What the hell are these errors? But if we do try to read the errors it might help us to understand what is going on.
Still difficult? Here is what's wrong:
We assigned the type of e
to React.FormEventHandler<HTMLFormElement>
, but whereas what the editor suggested is that the type of the entire function is React.FormEventHandler<HTMLFormElement>
and not for the actual e
parameter. To solve this, we just to move the type:
But if you want to only type the parameter, we can now hover over the type again and see what the editor is suggesting the type of e
be.
Get it? We were using the wrong type for the parameter e
previously. But now we have a better idea. We could now refactor the function to this:
We will do more questions on Generics (the React.FormEvent<HTMLFormElement> syntax) to understand them better. But at a high level, these are similar to how regular functions work. You can pass types as parameters to other types.
Here comes the interesting part: The App
component.
Migrating App component
Again, let us rename the App.jsx
file to App.tsx
.
Quite a few errors. But let us work on the functions addTask
and deleteTask
as these were already typed well in the child components.
We know that the addTask
just receives a parameter task
as string
and returns nothing. So, we can type it like this:
We will come to the other error within this function soon. But for now, let us worry about the deleteTask
function.
We know that the deleteTask
just receives a parameter idx
as number
and returns nothing. So, we can type it like this:
Great, now we have a single error to work on which is at:
But these errors are why TypeScript is so awesome. The issue coming because our tasks
state inferred the following type due to the default value:
But in the addTask
function, we are trying to add a task that has an id
that is a string. So, we more of have a potential bug in the app. Let us fix it:
There is still one issue that TypeScript is not complaining about.
It is technically not an issue. But can easily end up being one. We can see that having a dedicated type for task
might be a good idea and we can share this across all the components that use it.
Let us create a file called types.ts
and export the Task
type:
We can now refer to this type wherever we use it.
See how the React.useState
can use the type information on top of its initial signature? This is possible because of the generic type.
Great, now we have a single source of truth for the Task
type.
There is just one file left: main.jsx
.
Migrating main.jsx
Same as before, let us rename the file from main.jsx
to main.tsx
.
There should just be a single error now.
Let us try to understand this error.
The ReactDOM.createRoot
function takes a Container
as its first parameter which essentially expects a DOM element. But document.getElementById can return null if the element is not found. There is literally no way to check for type safety here and we have to just tell TypeScript to trust us and believe that this type is not null.
We can tell TypeScript to trust us that this will never be null by using the !
operator (called a Non-null assertion operator).
Remember if you are using the !
operator make sure to guarantee that this
will never be null. Otherwise, TypeScript won't be able to help you out.
That wraps up this TypeScript migration.
GreatFrontEnd has better questions!
We don't create questions because GreatFrontEnd already has the best possible library of questions and we recommend the same to our community.
Last updated on