- So to get started with this project,
the very first thing I wanna do is I want to navigate
into this after folder that I've created
and I want to create a vite application.
So we'll do vite at latest inside of this folder.
I want to do a vanilla type script project.
And we're just gonna install
and run these dependencies right here.
So once that's done, it should open up our project.
As you can see here, we just have the boiler plate stuff.
We're gonna remove all that
so it doesn't really matter what that looks like.
Now if we go into here, you can see let's just get rid
of essentially all the code inside of here.
We don't need this, we don't need that.
We're gonna keep the main dot TS
but I'm gonna remove everything that's inside
of it for now and inside of our HTML
we're gonna replace it with this HTML right here.
I'm just gonna copy
over the actual HtML we're using into here.
There we go.
And as you can see
we're importing the style sheet directly in here.
We can do that if we want
or we could remove that and import it in our main dot TS.
So we could say import styles dot CSS
And if we just make sure we copy that
over into here, you'll see
that it should import those styles for us automatically.
We also wanna make sure we up here import our script tag.
So we'll say we're gonna import that source
slash main dot TS.
There we go.
Now as you can see,
I have everything showing up and my CSS styles are there
but obviously nothing's happening
'cause there's nothing inside this TypeScript file yet.
So right now I'm gonna close
out of this placeholder version.
We don't need that anymore and I'm gonna close
out of all these before files 'cause we don't need those.
And I wanna look at the HTML to see what we all need.
So obviously we should probably select our form
'cause we need to make sure we perform an action
when we add a to do into this list here.
'Cause right now it just refreshes our page.
So let's get this form element on our page.
We can say constant form is equal to document query selector
and I'm gonna get the element that has this particular id.
Now I know this is a form element
so I'm gonna say HTML form element.
There we go.
Just like that as the type of this thing.
So now we can get that information
in there and then what we could do if we wanted
we could add an event listener
onto our form for submit, just like that.
There we go.
You'll also notice that TypeScript came in here
and automatically put that question mark syntax
for me because this could be undefined by default.
Now it's entirely up to you whether
or not you want to just add in the exclamation point
and tell TypeScript, Hey I know this is going to
be there so don't worry about it.
Again, it's entirely up to you which way you want to go.
We'll just do this exclamation point method
because if this isn't there, our code should fail
it should throw an error that way.
On the actual backend
if we have error reporting,
we'll see that error.
While if we have this question mark syntax here
the code wouldn't throw an error
but this form also wouldn't work.
So that would kind of be a bad thing to have happen.
But I think doing it like this is better
because if our code is broken
and for some reason this form isn't there,
it'll throw an error that we can report back
to with our error reporting software.
Now inside of our form what I wanna do is I
wanna get the value inside this import.
So we need to be able to select this input as well.
We look, this input has an ID of to-do input
so we'll just copy this line down, get our input.
This is going to be an HTML input element.
This was called see here to do input.
And I'm just gonna lead that exclamation the point
at the end just enforcing that hey
this thing is always going to be there
and if it's not, that's okay. I know that the HTML has this
so it should always be there.
Then what we can do is we can get that value inside here
but first I wanna prevent the default behavior of our form.
Then we can get our to-do title
or let's just say name it doesn't really matter
and we'll get that from our input dot
and we should probably get this input a better name.
For example, we can say this is our to-do input
so we can get the value from that.
Then I just wanna check to make sure we have a value.
So if our to-do name is equal to an empty string
then we're just going to return.
Now once we have that,
we obviously need to create a brand new to-do.
Let's create a new to-do
and this new to-do is going to have a name
which is just going to be our to-do name.
It needs to have a complete status.
We'll just set that to false.
And also we should probably use an id so we can
identify which to do is which for things like
checking them as well as clicking this delete button.
We're gonna create an ID and I'm gonna use the
crypto dot random UUID function.
That's just gonna generate a completely random ID for us.
Now the next thing we're gonna need is some way
to save this to-do.
So what we can do is we can create an array for
all of our to-dos.
DOS is gonna be equal to an empty array by default.
And then we'll add that to-do to our array.
So we'll just say push our new to-do.
There we go.
And you'll notice up to this point we've only
written two words of TypeScript in our entire
code base and everything else is working perfectly fine.
So we only have two lines
with TypeScript and everything else has been JavaScript
but we've still got all that really nice auto-complete.
Now obviously as we start creating more functions
we're gonna need more TypeScript
but so far we've got really good auto-complete
with just a little bit of TypeScript.
Now the last thing I wanna do inside
of here is to take our to-do input.
I wanna change the value back
to an empty string just so it clears itself out.
And if we do this and I type and I click add
you can see everything does seem like it's working.
We're just not rendering out any
of the to-dos in this erase.
We obviously have no way to see what's going
on with our different to-dos.
So we should probably create a function
that's like render new to-do,
which takes in a to-do and then it's going to render it out.
Now this is where we're going to want to
add some type script.
Because right now, our to-do has no type at all.
So we need to create a type for this.
We could do it in line if we wanted
but I think it'd be better to create a type for this
because our entire application revolves around to-dos.
So we should probably have a type for our to-dos.
And generally I like to define my types right
after my imports.
So we can say type to do here
and we know it's going to be an id,
which is a string.
We know it's gonna be a name which is a string
and a complete field, which is a Boolean.
That's how we've defined our to-do right here.
Now what we can do is we can say that our to-do
is going to be this specific shape.
Then we need to write all the code for rendering
this out and it should look just like this LI
element when we're done.
So I'm gonna come in here,
I'm just gonna paste down that LI element
comment it out just so we have a reference
to this when we're trying to write out our code.
So let's go ahead and let's create this first element.
We'll say constant list item equals document
dot create element.
We're gonna create an li.
And the really nice thing
about this is this list item is automatically typed
as an HTML LI element
since we're creating an LI element.
Then I'm gonna take that list item
take the class list and we're gonna want to add to it
this specific class right here of list item.
Then let's create the next thing in line
which is going to be our label.
So we'll say constant label is equal
to document create element.
There's a label and we need to add some class list to that.
So we'll say we wanna add this list item label
next we can come in here, we can create our input element.
So we'll say constant check box.
This is gonna be document dot create element of an input.
This check box is going to have a type here specifically
of check box.
And then we need to make sure we give it this class as well.
So we can say checkbox class list add
and we'll give it that class.
We can also do the exact same thing for this span element.
I'm just gonna copy this right here.
And this is gonna be our text element.
This is going to be a span. We don't need to give it a type
but we do need to give it the input of label text.
We also should make sure that this is our text element.
Now let's go ahead and we'll create our final element
before we start hooking everything up.
And that's going to be our delete button.
So we'll say delete button document dot create element.
This one's going to be a button
and we're gonna add some class list information to this
which is delete button.
There we go.
We should also give it the text here as well.
So delete button dot inner text is equal to the word delete.
There we go.
That should be all we need to do for this.
So I can just delete this code right here
'cause we've created all those elements.
I should probably put them all together though.
So lemme bring that back real quick.
We're gonna take our label
and we're going to append our input
which we called our checkbox as well
as our text element inside of it.
So we have both of these inside there.
Then we're going to take our li,
which we call list item.
And inside of that, I'm going to append our label followed
by our button, which we call delete button.
There we go.
And then finally, we need to add this
to our overall list right here.
So we need to select this UL.
That is our list all the way up here.
Let's get our list.
We know that this is an HTML UL list element
and this is just called list I believe.
Let me make sure.
Yep, it's just called list.
Now down here we can finally append all of that to our list.
So we can say we wanna add our list item to our list.
This is going to add an element.
It's not gonna have any data inside of it
it's just gonna be completely blank.
But if we call this and we pass in our to-do here
in our case we called it new to-do
we can get rid of this comment here
and now when I save,
I enter some information, click add.
You can see it added a brand new to-do
but all of our information is blank
'cause we're not using this to-do anywhere yet.
We should probably make it so that the text shows up
the checkbox is checked and so on.
So our checkbox here,
we want to make it checked based on our to-do.
So the checked property on this is going to
be equal to do to-do dot complete our text here.
We should make it so that the text content
or inner text doesn't matter which one is equal
to our to-do name.
There we go.
And that right there should already clean things up.
You can see here the name has been properly added
which is great.
Next we should make it so that when we click on this
it's actually going to be toggling the to-do property.
'Cause right now when I click,
it's not actually toggling my to-do,
it's only toggling my checkbox.
So what we can do is we can add an event listener
checkbox add event listener on change.
I wanna run an event
and this event is just gonna take my to-do complete property
and set it to whatever the check property is of my checkbox.
There we go.
That's just making sure these two things are now synced up.
If we didn't have this, the information
for our to-do would be out
of sync from what our actual checkbox information is.
And the next thing we need to do is our delete button.
Make it so that works.
So let's add an event listener for that.
This is gonna be on click.
What we need to do inside of this one is first
of all remove the element from the page.
So our list item, we can remove that.
Then what I wanna do is I wanna update my to-do's.
So I can say to-dos dot filter,
I'm gonna go through each to-do
and where the to-do dot ID is not equal
to my current to-do dot id,
then I'm gonna set that equal to my new to-dos.
My to-do's currently.
There we go.
So this filter is just removing the one to-do,
which is our current to-do
and then we're resaving our to-dos.
To do that we'll need to make this a let variable
and we also need to give this a type.
We'll say that this is a to-do array
so that way we know that this is an array of to-dos
and that'll get rid of all the errors that we had down here.
Now finally, if we give that a save
we can type inside of here,
click add, click delete.
You can see all that's working. We can even probably get rid
of this placeholder at this point. We don't need it anymore.
There we go.
Click.
That's working and that's working.
Last thing to do is make it to when we refresh
all that information is persisted inside a local storage.
We need some way to save our to-dos.
Let's just create a function save to-dos.
And this save to-dos function can just
use our global to-dos variable that we have
up here so we can minimize this down function
save to-dos just like that.
And this save to-dos function is going to go
into local storage.
So we'll say local storage, do set item.
This is going to be our to-dos and we'll set it to
JSON dot stringify of our to-dos.
That is going to save our information in local storage.
So now if we type something in click add
and check it refresh.
It's not gonna be showing up,
but it is being stored in local storage.
So now all we need to do is create a function
for loading our to-dos.
This load to-dos function is just going to set
or it's going to return.
Actually our to-dos are going to come from local storage.
So first of all, we should probably get our information
from local storage.
So this is going to be local storage dot get item,
all it to-dos
and that's going to be coming from there.
So this is going to be a string or it's going to be null.
And then we want to parse this as JSON
So we can say JSON dot parse our value
but by doing that we're actually gonna get an error
because JSON parse only accepts a string
and this could be a string or null.
So if for some reason this is null
what we wanna do is just return an empty string
and then we can parse our value.
But this is just going to return
to us any because JSON parse returns any.
So what we need to do here is we either need to do a bunch
of type checking to make sure this is perfectly okay
or we can just do a manual cast
by saying it's going to be as a to-do array.
In our case, I'm gonna do a manual casting
because I know for a fact,
that the object we're saving is our to-dos
which is a to-do array.
So there should be no chance that this is not a to-do array.
This is just one of the few use cases we're doing.
An as casting is probably okay.
So now we can load our to-dos
by setting this up here to load to-dos.
There we go.
Give that a quick save.
When I do this and refresh
you'll still notice nothing's showing up.
And the reason for that is we're never
rendering the to-dos that we're loading
so we should probably call render new to-do
and we should do that for each one of our to-dos.
So we'll say to-do's dot four each to-do
we can just call this function
so I can just pass it in there like that.
We also don't even need to manually type this
as to-do array anymore since this returns a to-do array.
Now you can see all of our to-do's
that are in local storage are showing up
but if I check them or I delete them
and refresh you notice none of that is being removed.
That's because in our render function,
our checkbox when we check it
we're not saving our to-do so we should probably do that.
And same here,
we're not saving our to-dos when we remove them.
So we should probably do that here as well.
Now you can see if I delete and check and refresh
add a new one, check that one refresh.
All that information is being perfectly persisted
and this is entirely working.
Now if we do a quick examination
of our code just to kind of see what we've written
you'll notice that the actual amount
of TypeScript code is pretty minimal.
We have this type right here,
which is just one single type.
We have three lines here where we had to
do a generic to cast a type as well
as this exclamation point to say that this type exists.
Then if we go further,
you'll notice this entire form event listener
there's no types at all inside of here.
This entire large function here
absolutely no types at all, no types here.
And then finally we have one single cast right here.
We have five instances where we use TypeScript.
We have a cast, we have, I guess technically right here.
We have a type.
So that's one other type that we have to worry about
but it's like six different instances of TypeScript
in our entire project and everything is working.
We have type safety everywhere.
And if I were to make a mistake,
for example, this I changed to be like names.
You'll now see that we get errors everywhere in our code
where that should have been named instead of names.
What really is super nice,
how great of a code experience we get
with such a minimal amount of TypeScript
and this pretty much scales to any size project
even as your project gets much larger,
the amount of TypeScript you write
is still relatively small compared to the amount
of JavaScript code you're writing.