Transcript from the "Will It Compile Quiz" Lesson
>> The next activity we're going to go through is kind of a quiz. You can think of it like a game and it's all about letting your brain operate as if it is the TypeScript compiler, right? So we're gonna try to spot whether these small code examples will compile, and if they won't, what kind of error we can expect.
[00:00:38] Hopefully this makes sense to everybody. All right, example 2, dealing with tuples. So we have a vector3, like xyz component, maybe. So we've got number, number, number, vector.push(6). Let's have show hands in the room, thumbs up or thumbs down, thumbs up means compile. So optimistic you folks. I see a thumbs up here and that is the correct answer.
[00:01:04] TypeScript will let us push into a tuple like this. And the reason is that this is a subtype of an array. So all the things that are available on an array are still going to work here. You can see here, it's sort of array.push, that's what we get.
[00:01:24] So tuples will kind of help us in terms of accessing things in terms of assignment, right? But, unfortunately, we can still push and pop and do these things that really, you wouldn't wanna do with tuples. Don't worry if you get these wrong, the point is, this is one more step towards starting to get it right.
[00:01:44] All right, how about this? We have a Color type with red, green, and blue properties. And then we have an interface called Color that introduces an alpha property. Thumbs up or thumbs down, will it compile? A mix of answers, the answer is, no. So interfaces are open. If we were to take this code example and open it up, and if I were to just change this to interface Color, everything would be copacetic.
[00:02:19] But once a type alias is defined, you can't have another type alias of the same name. They don't sort of smush together the way they would in the interface case. So c dot and you can see we get our red, green, blue and alpha. This is sort of the merging of interfaces.
[00:02:38] They kind of, this is declaration merging. Doesn't work when you combine a type alias, or if a type alias is involved at all, you'd have to have a different thing. All right, example 4, we have class with a class field called name, which is a string and friends, which is array of Persons.
[00:02:59] And in the constructor we say, this.name = name. Will this compile? No, why won't it compile?
>> Because the constructor doesn't include everything that TypeScript is expecting.
>> There you go, perfectly said, the constructor does not include, does not initialize all of the class fields here. So it has no initializer and it is not definitely assigned in the constructor.
[00:03:29] Definitely assigned, meaning it has to be able to see something in a synchronous code path. Very good, Example 5, if you have an abstract class called Person with an abstract class field name and we try to create a concrete class and the type of name is a string or a string array, will this compile?
[00:03:59] Just kind of comes down to, are we allowed to change this type or not? And if so, is the way we're changing it allowed? So first instinct, thumbs up or thumbs down? All right, see mostly thumbs down, the answer is, thumbs down, it will not compile. So the name field has to be compatible.
[00:04:23] In particular, we could have picked something that is assignable to a string. So if we instead, if we had a more specific type, like what if we did this? If we could do this with our new knowledge of, Tag template types. We could say, if I could figure out how to use my keyboard here.
[00:04:53] We could say, I'll take not just any string but any string that begins specifically with Mike. This will work when we subclass something like this or when we, I mean, we have subclass, right, this is an abstract class, we're free to use a more specific type tip, but not a more general type.
[00:05:11] So you can impose additional constraints, you can't relax constraints, cuz ultimately we have to be able to regard these as Persons, all right?
>> Would you be willing to just very briefly talk about what abstract means here?
>> Absolutely happy to talk about what abstract means. So an abstract class, the way I think of it, it's sort of falls halfway between a concrete class which would be just a conventional class and an interface.
[00:05:39] So an interface has no implementation, only types. A class is obligated to implement everything. You say I have a method and you not only have the type information there but you have the logic of the method itself. An abstract class lets us have a mix of the two.
[00:05:59] So in this case, I could have a function here like greet. Hello, this.name, right? I can have some pieces of this that are concrete, that have logic. But in this case, I could say, look, it's your job to bring me this field. I'm saying that something has to exist.
[00:06:25] Anyone who extends me has to fill in this blank. And I'm not gonna fill it in but I'm gonna tell you what I need. Makes less sense here but look at this, Abstract formatName. So I can have a function here, Like that and now it's almost like, okay, now student has to go ahead and implement formatName in some way, right?
[00:07:03] So sort of we get to have half and half. The reason that you don't see this that often is because you could also use an interface to state this type of information. But sometimes you kinda want an interface and a base class, and this would serve that purpose.
[00:07:23] Especially if you want a base class that cannot be directly instantiated, that's what this gets you, right? We could have created a situation where we had something like this, Person and then we have like, You bring this formatName up into the interface like that, And maybe like this, And string and here we could have had our base class.
[00:07:58] Now here, this is where it gets tricky, in order to have an implementation here that kind of assumes that this Exists, you'd have to say all right person implements I person. But now we're obligated to sort of fill in all of these blanks in some way on the space class cuz we have to make person complete now.
[00:08:20] So, there are use cases where this is like strictly a better tool for the job. So in this next example we have an interface called color with red green and blue numeric properties. Then we have a function called print color and we're passing this object literal into print color.
[00:08:39] So will this compile or not? I see, exclusively thumbs down as if people have seen the answer before. No, this will not compile. So, the kind of problem we're running into here is access properties on object literals. And here's, what's going what we're being told by the compiler and why it matters.
[00:09:04] So what we're being told is specifically that we're passing in something that has this alpha. At the end like a transparency channel. And that's not assignable to a parameter of type color and you might think. Well, wait a minute it should be red, color just simply asks of us that we have a red green and blue and we have more than that.
[00:09:30] What's the problem? Well, the problem is that, because we're using an object literal here, there is nothing else in this code that would be able to get a reference to the object being passed as an argument here. There's absolutely no, it's not like we're holding it in a variable or something.
[00:09:48] I can pop this out here so we can talk about it. So, let's see if we get the same error. Like this. And we're not going to, right? So in this situation we're holding on to this in a variable and we're not getting the error. So here's how I want you to think about this.
[00:10:07] Here's how I advise building it into your mental model. In this scenario on our screen, I can get at this alpha channel. So there is a point to it existing, there's a point to it being defined. We may use it somewhere else. It is possible for us to use it somewhere else.
[00:10:25] If we go back to the problem as originally stated there is no way for me to access this alpha channel. It is known if the types are telling the truth and if we use them as we should it's kind of pointless, right? There's no alpha here. And there's no other way to access this object that's passed into the function.
[00:10:48] So it's just sort of It's a vestigial thing. It's access. And that's why we get this error message. And that's why this rule is here. Because it's quite literally pointless for us to have this is less proper free, all right? In this next example, we have a type called color.
[00:11:09] And then we have a color value class that implements that type. Will this compile or not? thumbs up thumbs down. So red, green and blue. I see a couple sideways thumbs. I can see a yes them. Emma is that your name.
>> What do you think this will compile.
>> Because the constructor includes all of the properties that the type is expecting.
>> It does, what if I got rid of this public key word here? Would that change the way you think about this?
>> Okay, so your answers right, but there's there's a little extra curveball here.
[00:11:53] What we're seeing here are called puram properties. And the fact that we have this public key word here. It kind of automatically sets up this class field for us. So, this compiles and if we had equals new color value, something like this, and 000, we could see that CV.red, green and blue.
[00:12:16] These are here. So, when we have public red and public green, public blue, it's kind of shorthand for this. And we have a duplicate identifier because we'll need to get rid of this now. So when you have those access modifier keywords that public in the constructor signature, you're getting for free these class fields setup and this assignment happening in your constructor.
[00:12:59] It's a nice abbreviated way to do this. So, It'll work just fine. And you can see that, well, the tool tips aren't telling us the whole story here. But, this in the example you can see it how this is working. The fact that they're equivalent, all right? Moving on.
[00:13:22] Example 8. So we have a class and we have an interface of the same name. It looks a lot like our type alias example, right? Where we were kind of like trying to piggyback on the same word. Will this work the same way for class? What will happen?
[00:13:45] What's your gut reaction? I see get some thumbs up and thumbs down. So it turns out a class has the interface of the instance of the class, kind of riding on top of it. So really this boils down to interface and interface. So, first off, I missed this export keyword and without that, everything is fine.
>> [LAUGH] This is my past self throwing my future self curveballs. So, it also would have been fine if we did this, right? So if you have these merge declarations that are all sort of piggybacking on the same word, person. You either export them all or you export none of them.
[00:14:39] So this one didn't really have to do with the collision on person because that worked. I will read the next one very carefully, okay? Example 9. So we have a class with a field called name. And then a constructor. And then, we have some API call that's being made in the constructor.
[00:15:07] And then, so we get an API response back, convert it into JSON, and then set up the name on the class. Well the TypeScript compiler like this, thumbs up or thumbs down. I only see thumbs down in the room. And you might one clue is I have thrown a curveball here I'd like given something a sync in a constructor.
[00:15:36] So it stands to reason that there's a reason I'm doing that unless I'm just being devious. So this won't compile. And it's because, it is not definitely assigned in the constructor. And this is not a great error message because I would argue I can see an assignment in the code that's in my constructor method.
[00:15:54] The problem is, that code will not be invoked before we reach the end of the constructor function, right? It's this async thing API call comes back and all the while like what if the API call takes ten minutes to come back and here I have this instance of person.
[00:16:10] Send a name has not been set up on it yet, so this is why when we have a callback like this often things that happen inside that callback don't really count towards making sure class fields on the class are initialized. Now, you remember one of those first questions we started talking about where we had that promise constructor.
[00:16:35] We had the animal names and we were talking about the order they were logged out. And we talked a little bit about how when we create that promise, the callback given to the promise constructor is invoked synchronously. Unfortunately, there's no way to tell the compiler that, so if you ever create new promises in a class constructor like this, you may have to use the definite assignment operator, which is the bang equals sign.
[00:17:04] Because, as far as it's concerned, any callback has the potential to be a sync, and therefore, we can't say, yeah, name will have value by the time instantiation is completed. So we don't yet have a tool a way to express that like, this is a synchronous callback, maybe one day.
[00:17:28] All right, getting close to the end here. So this one I won't ask for a show of hands here I just wanted to kinda show you something interesting here it's very nuanced, but so we have three enums, and if you're used to using these you'll know that by default if we don't have initializers here you'll end up with numbers, if we look at Sublime Text, that's probably going to be a zero.
[00:17:55] So there's a weird thing that I ran into not while setting up this workshop project, but in another app where I learned something about innum. So this will not compile, and you may think okay, I'm using string values here that's a bit weird. The thing that's weird here is, where we run into the problem and where we escaped from the problem.
[00:18:22] So, here we're setting typescript. The first thing in the innum equal to this string Ts, and then we get this error message. Just it says, each member must have an initializer. And yet down here we get a zero cuz that's kinda the default behavior of an enum, and then vs code, and apparently that's fine.
[00:18:47] Now down, here we have things happening before and after. Our weird like stringing II knew what we wanna call this thing. But the fact that we kinda go back to numbers here, and we're explicit about it, that means that JSN is gonna be number four, I love bunkers, I have no other insight to offer you here, but it's kinda a weird a weird little thing.
[00:19:20] Keep an eye out for it if you're ever doing this. Another way to deal with this is just rest on the numeric values. To steer clear of this entirely use constants like immutable non assignable strings if you need string values. All right, what about this, we have a function called handle click maybe we're using this in like a React component.
[00:19:44] And we're saying on click equals handle click. We get the events target, and we cast it to an HTML input element. Maybe this is safe because maybe we know we're only using this on HTML input elements. That's the only way this function will ever get invoked. And then we say, if the value is not blank, set the value to the uppercase version of the string.
[00:20:14] So this might force an input to be uppercased, will this compile or not? This we're out of weird trick question territory now. So the answer here is it will not compile. And the key to look at is it's a freestanding function in which we are making kinda an assumption about this, for this type, all right?
[00:20:42] So often when you have a DOM event handler this will end up being the target of the event that was clicked or where a keystroke happened or something like that right. Anyone here familiar with react and have you used the class components of react where you remember having to bind those functions and pull them up?
[00:21:05] That's exactly why because you want the this in those react event hands. Learn to be the instance of the component. So you can set state, for example. You don't want the default behavior. But when you have freestanding functions and you need to rely on this, I'll show you how we can fix this code.
[00:21:27] This input And now everything should be copacetic. It's sort of the first, it's a pseudo argument of sorts, but you can say, make sure that something is of that kind. So if for example, we had HTML development Well that work? Maybe this won't type check as well as I thought.
[00:22:10] There's no. What about this, can I do. There we go. For this types of each signature are incompatible. So here like I'm saying I have a handle click that assumes it's only designed to work with an input, why? Because I need this dot value and that's part of HTML input element, that's the context.
[00:22:48] To the contents of the input ,so no it's not gonna work with the diff because at runtime this is gonna break or this will break here actually this will be fine this will be undefined and then to uppercase is probably what's gonna blow up. All right, last two questions.
[00:23:07] All right, here we have, again, a sub-classing situation. We have a private field called name, so I'm just gonna steer you into the important thing is to look at his name, subclass as a different type. And then we also have super, all right, which is calling the constructor of the base class.
[00:24:20] You could have a parent class, and a sub class, they each have their own, they're not related, they're not even visible to each other. So those are fine, there's no collision there, this will actually collide, private age like the fact that they both exist, is in and of itself a little bit of a problem.
[00:24:37] Because, this should really be protected, protected as the keyword that lets us share the property between base class and subclass. So here at runtime, this is gonna be the same property, but you're sort of giving two different things in an inheritance chain, the ability to write to it.
[00:24:55] So this is why you're sort of forbidden from writing over these fields, also the types of age are not comparable, so this piece is visible from within this class itself, right? So we have a nullable age here, and then we're expecting it to be a number, now, why didn't that give us trouble here?
[00:25:19] It's because we're defaulting to a value of zero in the event that it comes null or undefined. All right, last example, so looking at this again, we've got a name on a person, we've got a function called make name. Which takes either an array of strings, or a string and kind of concatenate them together or just passes it straight through.
[00:25:43] All right, now we have a student which is a person, we have this name is a string or an array of strings, we already determined that this and this up here will not collide. So the question is, is this gonna be okay, and is this gonna be okay, thumbs up, thumbs down.
[00:26:12] Thumbs up, thumbs up, I don't see any thumbs down, I believe this will compile, and we kind of went through all of the reasons why, so this type here lines up with this type, so we're good there. But this is a good example of like, it's fine to break out a little function here and to use it in the constructor Or even the super call which generally should be the first thing.
[00:26:35] When you're inheriting, call super, its fine to put that there and this serves to normalize things so that we can hold one type of value in our subclass, but the base class has sort of a more constrained type