Making TypeScript Stick

Class Property Inference

Making TypeScript Stick

Check out a free preview of the full Making TypeScript Stick course

The "Class Property Inference" Lesson is part of the full, Making TypeScript Stick course featured in this preview video. Here's what you'd learn in this lesson:

Mike demonstrates the ability to no longer add types to every class field with class property inference when noImplicitAny is set to true. A student's question regarding what happens when there are conflicting types in the constructor and declaration is also covered in this segment.


Transcript from the "Class Property Inference" Lesson

>> The next new language feature that we'll talk about is, this one's easy to understand and a welcome improvement. That is class property inference from constructors. And what this means is you no longer have to do something that used to be mandatory. And that something, that you used to have to do, was add types to every class field, right?

So this, what you see here, is possible today. But previously you would have had to say, red is a number, green is a number, blue is a number. And it sort of added to the verbosity of any kind of class that you would set up, right? You need types for the arguments, you need the assignments inside your constructor.

You needed the class fields with their respective types. So what's happening now is, based on what happens in your constructor logic, the types for those fields will be inferred. So if you upgrade to a sufficiently advanced TypeScript version, you can start going through your code base. And, where appropriate, delete some of those class field types.

That'll be figured out for you automatically. Now, what I advise doing here is, a simple situation like this, absolutely, delete those class fields. I mean, delete the explicit type annotations for the class fields. However, if you have something here like a complicated ternary operator based initialization, I might still leave the class field type here.

For the same reason that I like to keep explicit return types on my functions. It's kind of like you state that you have intent for this field to end up as a particular thing. And the compiler will help ensure that you follow through on your intent, right? So if you've ever written a function, and you start adding a couple little branches in there, a couple conditionals, then there's one way you can go through this function where you return null.

But you stated you would end up with a number, no matter what. It's nice to have TypeScript right in that local area bust you on that. So for simple assignments, I don't think you need an explicit type annotation. But for anything that gets a little hard to immediately look at and understand, leave it there.

And the end result is, the type error will happen in this location of your code. As opposed to, somebody is using type green,, and over there, way, far away, that's where the type error is happening. That's the effect you get, right, through that explicit type. You want the error to happen where the fix needs to be, yes?

>> If you have conflicting types in your constructor versus your declaration?
>> Yep, so in case I did this too fast, I just hit this Try button. You can do this on almost all of the code examples on this website, where you can pop out into the TypeScript playground.

So I can answer your question by doing exactly what you asked me to do. So here, let's say that this is a string. And we'll get a nice little type error here saying that, hey, c is a tuple of three numbers, and something weird is happening here. So that, I feel like this is a good error, it's close to the location of where we need to apply a fix.

The strange situation that I was talking about earlier, let's say we did this. Sorry, let me be a little more sane in terms of how I write that. And we did something like this. So here, there's something more complicated happening. Where the type of green might not end up being a number, it could be a tuple of three numbers.

And let's say that we left this out, and we said, I just want inference to handle all of this for me, we get no error messages here. The only place you'd get an error message, Is something like this. And you wouldn't even get an error message here, you'd have to have something like number.

And now, hey, something's a little off here, green could be a number, but it could also be this other thing. And I don't like it when my errors happen far away from the location of where the fix needs to happen. So this is the basis for my advice.

If there's anything more complicated than just a simple assignment, especially if you have some sort of control flow happening, right? Some two different branches that you could take. Just go ahead and leave this on. Leave that there, and now you can see the error will go away down here.

Because our intent is that green is a number, we're stating that upfront. But this is where the incongruity happens, and that's where you need to make the fix. Errors where the fix needs to happen, that's the best thing, right? That's where, if you use VS code, you'll have that nice list of problems, you'll be able to click on that.

That's where you need to go and do something, the world is better. So that's all there is here, it's very simple. I think it's a straight up win in terms of having to write a little bit less code.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now