This course has been updated! We now recommend you take the Deep JavaScript Foundations, v3 course.

Check out a free preview of the full Deep JavaScript Foundations course:
The "Challenge 1: Solution" Lesson is part of the full, Deep JavaScript Foundations course featured in this preview video. Here's what you'd learn in this lesson:

Kyle walks through the solution to Challenge 1 and takes questions from students.

Get Unlimited Access Now

Transcript from the "Challenge 1: Solution" Lesson

>> Speaker 1: Let's talk about Exercise 1. So we'll kind of walk through a few of these things. The readme said to check to see if there were strings that should have been numbers, or vice versa. There were a couple of places where that ended up happening, so I wanted to point those out.

[00:00:16] The first is, if we look in this click handler right here, the one that starts on line 10. This click handler's what happens when you click the button to add the entry that you've put into the form. And you'll notice that we're pulling those values directly out of the form and then passing them onto to this add to work function on line 23.

[00:00:38] We're just passing them along as is. Now the project ID and the minutes are actually numbers. And there are places where we rely upon those to be numbers so that they are matching correctly, in particular the project ID. We hard code the project in, like the project name.

[00:00:58] But the ID for the project when it's generated, if you come to the addProject function, you'll notice that that's a randomly generated number. So whatever number is passed in from the select box, we need to make sure that it's a number, because there is gonna be a checked on that check up and is right here.

[00:01:18] To check to make sure that the project ID that's passed in is the same as what's in the project's element. And you'll notice I'm using a triple equals there to make sure that it exactly matches. So one option would have been for you to relax that with a double equals.

[00:01:33] That's one way of approaching it. And typically speaking, I would generally say, yeah, it's not a bad idea for there to be a double equals there. What difference does it make whether it's the string or the number equivalent? But I'm actually gonna do it the other way because there's a bigger question at play.

[00:01:49] Because minutes definitely needs to be a number. So as a rule of thumb, I don't want to pass around or propagate a piece of data through a system if it's not yet in it's appropriate type representation. So the first place for me to fix that is right here.

[00:02:07] So I could fix that by explicitly coercing both the project ID and the minutes to the number form. So I'm gonna use the number function to do that.
>> Speaker 1: Now I don't need to change that triple equals to a double equals because we're ensuring that in all places where the project idea's compared it's a number.

[00:02:33] So the triple equals is fine. You could also simplify it to a double equals. Based only on the notion that if they're both numbers, there's no reason for the triple equals here. The double equal suffices cuz they're of the same type. So either way you wanted to go about that, you could set it as double equals or leave it as triple equals, okay?

[00:02:54] Now that we've made the minutes into numbers, you might have then started to notice, if you tried it, you might have then started to notice that when it was printing out the time, it was clearly doing some concatenation with like an undefined value or something like that. That's because currently, the addition of those minutes is not happening with the value that is already guaranteed to be 0.

[00:03:20] So for example, right here when we keep track of the total project time, we're just saying project time, essentially incrementing it by that number of minutes. But if projects.time has not already been set to some number, then now we're concatenating or adding undefined with minutes, and that's gonna end up as name right cuz unrefined coerces to name.

[00:03:40] So there's a couple of ways you could have solved that. You could have, in the project entry, you could have forcibly set down here, you could have forcibly set time to 0. That's one way of doing it. You could initialize it to 0. The other way of doing it which was a little bit more coercive Is to coerce this thing to definitely be a number or to ensure this is definitely a number using what we know about coercion.

[00:04:05] Here the default value idiom might have been appropriate. So we could have said projects.time or 0. Cuz if it's not already set, or even if it was set, but it was set to 0, it doesn't matter, we can use 0 as the base point for the numeric addition.

[00:04:20] So the default idiom seems to be a little bit more appropriate here for encoding our intent., which is our intent is if it's not already set, just use 0 as the base point. There's another place where that happens, and that is, check where we add up the work.

>> Speaker 1: That happens right here. Again, if the project entry doesn't already have the time, so we can default idiom, the project entry to 0.
>> Speaker 1: Okay, so that should take care of the items, unless I'm forgetting something. That should take care of several of the items. But there's a couple of other places where things still wouldn't be working correct.

[00:05:07] In particular, you might have discovered that the output when we formatted the time, the output seemed to be incorrect. And it's because I intentionally have the order of operations here not safe to the type conversions that we're doing. This is something obviously that TypeScript would have caught for you.

[00:05:24] So this is one of those examples of places where people would say, well TypeScript would have let me know that my variable was changing types. But you'll notice that here I changed minutes to be a string, and then I rely upon minutes to be checked to see whether at 0 to just return the empty string.

[00:05:46] You might not have caught that unless you tried to enter in no time for an element and it should have printed out as empty, and it might have been printing out as something different. Perhaps with a nan or something. So here the order of those two operations needs to be swapped.

[00:06:03] Because we can't rely upon, now line 150 needs to rely upon minutes still being a number if it's gonna do that comparison of getting 0. We can't change it to be a string. And especially a string would have other stuff in it. Or it wouldn't compare.
>> Speaker 1: Okay?

>> Speaker 1: Next we need to look at the validate work entry function and implement those validation rules. First, the rule is that the description text needed to be at least five characters long. Rather than re implement this function, I'm simply gonna switch to the solution file and show you how I did that.

[00:06:45] You'll notice that description.length, if it's less than five, I return false. I have a bias here in my validate functions to always return a boolean, a false or a true. Sometimes people just do an early return and then rely upon the undefined that gets returned as coercing to a false value.

[00:07:09] Because you'll notice when you use the validate work entry here we just do a boolean check against it. So you could just do a return here without anything, but I think this more clearly communicates the intent. I'm affirmatively saying the validation failed by saying return false, okay? And the same down here with true, I'm saying if we get to this point, the validation clearly succeeded and I'm returning true.

[00:07:34] Make sense? So my description length check, if it's less than five, that's a failure. The readme says to also make sure that minutes has to be a non-empty value ignoring the leading and trailing white space. So I kinda knew right off the bat, okay, I'm gonna need something like a regular expression or something like that to ignore.

[00:07:57] The first thing is, if I enter in something consisting only of white space, that's what this check is doing. If I have something consisting only of white space or if it's empty, cuz I used the star here, then that lets me know that this was a failed entry.

[00:08:15] You'll notice in the read me it says leaving minutes blank is not valid, it's invalid okay? So I wanted to check to make sure that I wasn't leaving minutes blank. I also wanted to check to make sure that what was being Put into minutes could valuably be coerced to a number.

[00:08:34] So I first coerced it to a number and then checked to make sure that it wasn't NaN. I used a number.isNaN, could have also used object.isthere if you prefer. And then I checked to make sure that minutes, given that I knew that it was in the correct range, I checked to make sure that minutes was within the appropriate range where it wasn't less than 0, so it wasn't negative and it wasn't greater than 600.

[00:08:55] That allows me then to set a minutes of 0 if I didn't wanna log any time against that particular work action, okay?
>> Speaker 1: The less than and greater than here are taking advantage of implicit coercion. I did not, at the top of my validate function, explicitly coerce minutes to a number, and then do my checks against numbers.

[00:09:21] Why didn't I do that? Anybody spot why?
>> Speaker 2: Cuz you have-
>> Speaker 3: Is it the?
>> Speaker 1: What's that?
>> Speaker 3: The regex?
>> Speaker 1: Yeah, because I wanted to test, if I hit coerced minutes to a number, like if I say minutes= Number{minutes}, all right cuz the first time I was making this exercise that was my first approach.

[00:09:43] And then I thought wait a minute. What happens if somebody leaves it blank, it's an empty string, and what's that gonna coerce to? That's gonna coerce to zero, zero is supposed to be an allowed value here according to the validation rules, that's an allowed value, but empty is not an allowed value.

[00:10:01] So I can't coerce first before I check whether or not it's been passed in as an empty string, make sense? So I needed to do my check first against the string value, and then do my check against the coerced value. I took advantage of the fact that on lines 35 and 36 here that minutes, that the less than and greater than operators will automatically coerce those to numbers, since they're doing a comparison with numbers, okay?

>> Speaker 1: And that represents the work to do on the exercise. So what questions can I answer about exercise one?
>> Speaker 2: Can you please explain, what happens if we remove the line number is NaN.
>> Speaker 1: This one?
>> Speaker 2: Yeah, I mean, what case, I just don't quite get what case that [INAUDIBLE] correctly.

>> Speaker 1: So if we compare a string with a number, like if minutes came in as a string, and it was something other than a number representation, then what would happen. Let me just. I'll literally comment it out. I gotta remember to undo it. I'll literally comment that out.

>> Speaker 1: So if I type in a you notice that it allowed it through. Now why did it allow it through? That obviously ended up failing with this name thing. Why did that pass through the validation function? Well, first of all, it failed this check, right? But then we asked the question, is quote A less than zero?

[00:11:40] That was the first question, and when there's a string involved, it's gonna actually do a lexicographic check which is alphabetic character check in ASCII code. Quote A is not less than zero. And it's also not greater than 600. So it failed both of those tests. Because NaN, even if it was doing the number check NaN is not less than 0, nor is it greater than 600.

[00:12:08] So lexical graphically or numerically, NaN would pass through just fine here. Which is not what we want. So we need to test for the NaN.
>> Speaker 2: So in the comparison, it was converted to a number. Which would make it a NaN. And then automatically.
>> Speaker 1: Well, let me answer that definitively by modeling for you how you should answer these questions.

[00:12:35] If you don't have somebody's book handy, which we could go and look at my book but let's just see what the spec has to say. It is section.
>> Speaker 1: 7 of the abstract operations. And operations on objects.
>> Speaker 1: So these are those algorithms. Remember the abstract equality one, right?

[00:13:11] We looked at that one already. 7.2.13, Abstract Relational Comparison is the less than and greater than. So let's see what this algorithm says. See if I can figure out what it's talking about here. The comparison x less than y, where x and y values produces true, false, or undefined.

[00:13:30] In addition, it takes a Boolean flag named LeftFirst as a parameter. We're gonna assume LeftFirst. So we were asking if the string A was less than 0, why would that fail? So it said if the left flag is true which it would be, then we wanna take primitive of x which it's already a primitive.

>> Speaker 1: Okay so here it says if both comparisons are already strings then it's gonna do a lexicographical Comparison. That's what it's doing here. It's gonna look at string characters and do that comparison. Otherwise, it's gonna make them both into numbers. So it would have taken that quote A and made it into a number.

[00:14:14] And then it had the 0, and it would have said is NaN less than 0? So then we have here.
>> Speaker 1: See, nX. nX would be NaN, because it was the quote a that got coerced to a number, so now it's the number representation of it, and it's saying if nX is NaN, return undefined.

[00:14:37] So that less than operation wouldn't have returned false, it would have returned undefined. But undefined is a false value right? So that would have failed that condition. The exact same logic applies when we try the court a greater than 600. Is that it's gonna make quote A in demand and NaN is not greater than 600 for the exact same reason.

[00:14:59] So both would have been undefined and undefined. And both of those are false. So both those test fail.
>> Speaker 2: Just, my approach was different, I used a return and actually compared that this is greater or equal than 0 or less than equal to 600. That's why it worked in my case.

>> Speaker 1: Okay, so the greater than or equals actually does something interesting and I won't get fully into that. This is covered in the types and grammar book, but we think that greater than or equals does the same thing as doing greater than or an equality comparison. Turns out greater than or equals does the less than and negates the result.

[00:15:42] So what was the last thing? The last thing was undefined.
>> Speaker 2: Was undefined.
>> Speaker 1: And then we would've negated that to make it true. So that's why it worked. It's one of those happy accidents for you.
>> Speaker 3: So as for kinda a bonus for this exercise, you ask how would you go about writing simple tests to test the behaviors of this application?

[00:16:01] How would that be different from the function validate work entry is the function validate work entry just kind of a data validator.
>> Speaker 1: It's an input validator, but that's not testing the application. What I meant by testing the application is if I call the add work entry to whatever that function's called,

>> Speaker 1: Add work to project. If I called add work to project with a certain set of input, does it actually end up producing the DOM elements that I expect in exactly the right place with exactly the right contents, right? That's kind of an integration-level test. There are also unit-level tests that could be performed on several of these.

[00:16:40] Like, for example, the findProjectEntry, I could validate that if I pass in a specific projectId, I get the project entry, if I pass in a nonexistent one I get nothing. So there's both unit test and a creation test that you could do here. I wasn't actually expecting you to do that.

[00:16:55] I just wanted you to think about how would I test this project. I'm glad you brought that up though because as we get further along here, you may find it to be useful because this is a certain thing that I did when I was writing the code. Did I go back and fix that?

[00:17:11] I think I did. You might find this useful. Nope I didn't fix that, sorry.
>> Speaker 1: That should give us an error, and it does. So what I did is I was doing this to make sure as I did each iteration of this project that it still did the correct thing, is I actually had sort of a unit test or integration test, I guess more integration test that I just ran in the console of my browser.

[00:17:51] So I basically called a set of calls, I said addWorkToProject, and I said since I knew project was a public array I could get at whatever the ID was that was randomly generated for the first project. And then give it a really long comment and then some number.

[00:18:10] So this was basically my unit, or my integration test that when I ran that, I expected to see exactly this behavior where I had 114 is my output and the 36 or whatever. So I just visually made sure that every time I made these changes to the code that this code ran and gave me the exact same results.

[00:18:29] I didn't verify it fully as a test suite, but that was kind of my ad hoc testing. Then they'd be useful to you as you go through exercises two through eight to come up with some kind of test like that for yourself. Okay, so that represents our first core.

[00:18:44] The types and coercion system. Again, the takeaway there is to hope that you will look at types and coercion with a fresh set of eyes, and ask, how can I use that appropriately, as opposed to just ignoring that topic or making a linter, remove any possibility of it?

[00:19:02] Where are places I can use my knowledge correctly to make my code better, more expressive, more readable?