TypeScript and Vue 3

Compiler Macros: defineProps

Ben Hong

Ben Hong

TypeScript and Vue 3

Check out a free preview of the full TypeScript and Vue 3 course

The "Compiler Macros: defineProps" Lesson is part of the full, TypeScript and Vue 3 course featured in this preview video. Here's what you'd learn in this lesson:

Ben demonstrates using the helper method defineProps to generate prop types. Vue provides this method for added functionality that does not affect the resulting bundle.


Transcript from the "Compiler Macros: defineProps" Lesson

>> We've talked a lot about moving data methods computed all of you have done that successfully. But the one thing I have intentionally left off because it's kind of different in the script setup is the idea of basically what our compiler macros. So what our compiler macros Well, let's go ahead and open up the nav bar here.

And so let me go ahead and now hide this Yep, perfect. Okay.So let's go ahead and convert this over to TypeScript real quick. So script set up, Lang Ts. And then, save, that's gone? That's gone. Okay, we're gonna comment out the props for a second. And then we're just going to quickly say const nov list, equals a ref.

And rap it just like that. And so here's an example, right, where we're progressively enhancing a, whoop, I have the wrong thing here. Here we go. We're progressively enhancing the component you can see here with TypeScript. But you'll notice though that, actually let me import ref real quick, so that y'all can not be distracted by the red squiggly.

I'm not actually going out of my way to define a custom type for what is inside of this nav list. Because, first of all at this moment, it's fairly straightforward. And if we look at the ref, it already knows it's a name and a path. Now granted, if over time you start building like nav items and you want it to follow a specific structure, there's certainly a rationale to do so but this is where It's important to try to differentiate when something is valuable to do and versus when something is just done for the sake of purity for purity sake.

Now, that said, we still haven't defined this props though and how do we do that? And so compiler macros essentially our views way of providing you functionality that will not impact the built the bundle that's ultimately output it And so we have this basically a helper function called define props.

And this again once again, this function is a compiler macro that's specific to script setup. So you actually can't use this or you shouldn't really be using it outside of setup. And so inside of this, this this define props. What we can do is similarly to how we normally define prompts, we can just say tagline And then we can do things like type: String, and required: true.

So this is where define Props, this looks pretty familiar. So we take that concept now, and let's look at something a little bit more advanced Let's go ahead and take a look at the restaurant card. Here is where we can actually start to yeah, let's play with this one.

Okay. So on the restaurant card you'll see here that we have a prop. That's a complex type. It's an object that is supposed to be a restaurant. And it also had this amid thing which we'll talk about in a second. And then otherwise we need to go ahead and move this over.

So, let's go and do that real quick. We'll have set up we'll take care of that let's comment this block out first. And then once here, we have const status Color = computed rap that Arrow function. That's it. And then here we have our delete restaurant and then that is a function.

And then we will comment this out for a second because this won't work. Okay? Okay so here we have everything and so using what we just learned about compiler macros we said that there was a defined props helper helper method right and so one things we could do is we could do the same thing as before we could say restaurant And it's a type object as prop type restaurant.

And then we can say require true, right this seems familiar. But what if we want to do this a little bit more like natural TypeScript? Well, one of things we can do is we can actually provide the types to define props. As a basically through type casting. So in other words, we could, for example, call it prop types.

I'll just call it like that. And we say there is a thing called a restaurant that is of type restaurant just like that. And so the way this will look inside of your props is rather than defining it inside of the parameters of the parentheses If you use the greater than less than symbol brackets, and then you just pass in Prop types and then this provides you a little bit more of that like organic type script field.

However, one of the things you might be thinking of is well, okay, so for example, here we are, this is pure TypeScript world. So if you we can't just go like this is actually a type restaurant and we can't be like required true you know default whatever this is not going to work because this is not an actual JavaScript object so, If you want to do something that's required, remember that inside of typescript as long as you define a property on an object, it's by default required.

So that's already on. If you actually want it to be optional, then this is where you might put the question mark as per the part of it. Actually not Oops, I forgot. Sorry, go back here. You would want to put it like this. And then you're saying that this prop is optional and not required on the component.

The thing though, is that something that we often do do when it comes to props is we want a default value. And so this is something that's currently in an experimental phase, but I'll show you how I've seen some of it done before. So there's two ways to do this one is that there is I forgot which Bill this was but at one point, there was a proposition to do it like this.

Where you had this use defaults are used with defaults helper, and then you would pass in another object that was like restaurant and then like default values. For me this is kind of clunky cuz you have this like define props and you're wrapping it with use a default that you're passing in a second argument that happens to be it feels a little.

Feels a little clunky. So the way that's currently being experimented with here in the docs, which I'll definitely link to, is, let me blow it up a little bit, is it's basically reactive destructuring. So basically, you would actually define your props as normal. But then as a result of using normal JavaScript, where you destruct the process you would normally wanna use you just assign the default like values then which is actually fairly, like one to one with actually what we come to expect with JavaScript actually.

And so, granted again, at least at the time of this recording, this is an experimental feature that needs opt in inside of your build. But this is the direction it does looks like. This is the direction that D3 is heading as far as using defined props so that you can provide default values.

In the meantime, though, if you really want basically what I would recommend just to keep things simple, did I delete it? I did delete it. Is while the casting is nice. I honestly don't. I think for the most part, it's totally fine to just keep it as is to just say restaurant type object as prop type restaurant, and then just a fine your default here as part of your definition.

There's nothing wrong with this. Again, we'll find better ways of doing it but for now, if you really do want to default this is one way to do it. That said, there's nothing stopping you as well from like assigning another reactive value that will then like take the profit if it's empty set it, as we know programming, there's a lot of different ways to do things.

So, don't feel tied in, but for now, we're gonna assume there's no default. So, it's totally fine, we'll just use his typecasting, to do, define prompts. Okay, it's gonna delete that, can delete that. Okay, now we've define our props. And then actually one sec, I need to actually import computed.

So this thing stops yelling at me. And then we have. Okay, here's the next problem though. You'll notice though, that inside of options API, we typically could refer to the props directly because it was on the disc object it was automatically inherited. But that's no longer the case because we have it where we have it inside of defined props.

So how do we get access to it? Well, we create a variable that we want to reference. So typically it's this constant props. That's simple. And then that way your, this becomes props.restaurant.status. And so you'll see though, which is just interestingly enough. Is that TypeScript is yelling at us.

It's saying that hey as of right now and based on how you defined the props it might actually be undefined, so I can't actually run this block of code. And so this is where type guards come into play so if we want we could say, if props dot restaurant exists, then run all this code, right?

And then as you can see here with that type guard, TypeScript knows that it can assume that type restaurant will have its see all the properties that it needs and then it can run through and that's actually helpful because as we know when you build your code sometimes we do miss these little edge cases and things do break because we forgot to like yeah in this particular data type something like this needs to happen before something like this happens type two does help you to kind of remind you of that.

However, in our case, this is a required prop. So in that case, the type guard is really not necessary. So this way you can see it's not yelling at me cuz I'm promising it, that it will get the value that it wants.

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