Check out a free preview of the full Intermediate TypeScript, v2 course:
The "object & Empty Objects" Lesson is part of the full, Intermediate TypeScript, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses the object type which represents all possible values except for primitives, and it can accept functions as well. Various examples and use cases of the object type and how it can be used to remove nullability from a type by intersecting it with the empty object type are also covered in this lesson.

Get Unlimited Access Now

Transcript from the "object & Empty Objects" Lesson

[00:00:00]
>> Let's talk about an almost top type. This type is called object, like the word object. So there's a little bit of overloading of terminology here in TypeScript. The object type represents the set of all possible values except for primitives. So we can see here that it will accept an object containing a field called status.

[00:00:28] It's not happy with the string, it's not happy with null, it's happy with a function. A function is an object, it just happens to have a call signature on it that we can invoke to call the function. I'd say this is almost a top type, right? The only things it can't be are number, string, boolean, null, undefined, symbol, and big end.

[00:00:50] Those are all the primitives in JavaScript. So it's like all possible values except for seven kinds of things. So I told you that there's a little bit of terminology overloading here. We have a type that is the word object, but also sometimes people say interfaces are used to describe object types.

[00:01:12] So these are not the same thing in this context. So what we're doing here is we're creating something here that has a union type baked into it. So I've got a success shaped thing, and then a fail shaped thing and it could be either or. We know that we can't use union types with interfaces, like an interface cannot describe a union type.

[00:01:36] But if we look here, val is of type object. And it's perfectly happy to receive this, actually I should, just to make sure I'm being honest here. Sticking to my evidence based approach. There was some inference there that was stopping us from seeing this for real. Yep, good, it's still valid.

[00:02:00] So here's response, just to explain what I just said. TypeScript was being too clever and it was saying, hey, I know you described that this is the type here, but but I also see that you're just giving it this value. So I'm just gonna assume it's this one shape, and when I hovered over this all we saw was this, this this first piece, right?

[00:02:22] So now because I've casted it, we can actually see that the union type is present here and assignment happens perfectly well. Val, which is of type object, is perfectly happy to accept this. So don't get this confused. When we say interfaces represent object types, that's different than this, the type called object.

[00:02:55] The reason I'm making this point so clearly is, some of you will get trolled by this. And maybe you'll remember talking about this and it'll save you from spinning your wheels. So TypeScript considers null not to be an object. I know that in JavaScript null is an object to due to the historical bug.

[00:03:18] So let's check our tsconfig here. This would be the first thing I'd look at. I'm gonna turn off strict null checks. So whether or not null is allowed in an object type, yep, whether or not null is allowed in an object type just has to do with this strict null checks, tsconfig settings.

[00:03:41] This setting here, if set to false, if there are no strict null checks, null is allowed as part of any type you create. This is why you wanna enable this to true. So I would just say, object is not special when it comes to null, the inclusion of null inside of the values it represents, it works exactly the same as you would expect strict null checks to behave in other cases.

[00:04:18] And there, you should see up here, 65, yep, it's erroring now. And this fits the previous definition that I had, right? I said it can't, it's not happy with primitive values. Null is one of the seven primitive value types, undefined as well. Good question though. All right, one more almost top type.

[00:04:45] This is even topper than the previous top type. So this, I'm gonna overload this even more. I wish I had different terminology to share with you, but this is the terminology. This type here, the open close brace, this is called the empty object type. It is still different than the previous two object type terms that I've described.

[00:05:13] It's not the one that pertains to interfaces, it's not the word object. This is a third distinct thing. It's common to think that this is interchangeable with that type that's called object. In many cases it is, but not all. So, what is the empty object type allowed to accept?

[00:05:36] It is happy to accept all possible values except null and undefined. So you can see here, we create a bunch of different values up here and I've named them so that we don't have to stare at these and understand them too much. The variable names describe what they are.

[00:05:55] So we create val2 which is of type empty object. It accepts four, it accepts a string, it accepts a date. It accepts a string or number. Here's a nullable string. And I'm gonna do, TypeScript again getting super clever here. It's saying on line 78 I assigned it to null, I'm gonna flip that around.

[00:06:16] Good guy TypeScript screwing me up. Good, I mean, it's still unhappy. It's funny how it was happy before. It's almost like the tooltip was lying to me. Like this, it knows it was null, that's why. I hate to say, oops sorry. I hate to think what would happen if I did this.

[00:06:49] This is where we would have gotten screwed up, what if I do this? All right, now it's, all right, you've set this to null now. Maybe it's saying, I see what you initialized it to and I don't see any code that could have possibly changed this value between here and there.

[00:07:12] And so I can say it's a string, but here we're actually getting the null case. It's now confident, I see the null. I know it's null. But in any case, I hope you get the point here that it's anything that has the possibility of being null. And here's object.a, which that's an optional property, it has the potential to be undefined.

[00:07:39] The empty object type is not happy with that. Undefined is not assignable to empty object. A null is not assignable to empty object. So that's the empty object type. I say it's even more even closer to a top type than the type called object because that wasn't happy with any primitives, right?

[00:08:00] Now we're even more accepting of different kinds of values. We can take strings, we can take numbers. We can take a big ends, we can take symbols, we can take Boolean values, a lot more things. So this is this is a more expansive, an accepting type. A practical use of this type cuz I may feel like I'm throwing a bunch of things.

[00:08:27] It's like we got all these things on the menu, what do I use them for? Empty object is actually quite useful. So here we have a bunch of different example types. We've got one that's a number without undefined. It's just got the empty object or null. This one is with undefined, so it's empty object or null or undefined.

[00:08:53] And then here's an unknown. So what we can see here is the fact that this is unknown, we can't assign that to this value here. Why do we think that is? So this could be anything. In terms of the values it could accept, it could be anything. What's the problem here?

[00:09:17]
>> If it's undefined, it wouldn't work.
>> If there's literally one value in JavaScript undefined, that unknown could include which empty object or null does not allow. That's very good. The error message doesn't say undefined, but that's it. And look down here, now empty object or null or undefined, now we're back to a top type.

[00:09:44] Remember empty object, it's everything except null and undefined. We've added those back in, and now this is perfectly happy to accept an unknown. So that's kind of interesting. I mean, it's interesting, it's not useful yet. All right, let's look at a scenario. Let's say we have something like this.

[00:10:03] It's a nullable string or number. And I'm using the word nullable here to mean it could be null or undefined. It could be something that indicates value is missing or value is not present. We can use an intersection type with the empty object to effectively remove null and undefined from a type.

[00:10:29] This is a very, very useful type. You can use it to remove nullability You wouldn't be able to do this with a truthy falsey check cuz of course, you can have empty string and zero. But this lets you remove nullability. It's as if we narrowed in a condition where you said, all right, is the subject, as long as it's not type of the subject is not undefined and type of this object is not null.

[00:10:59] Here's a block of code I want you to execute and you'd get string or number. There's a utility type, I think it's called non nullable, let's test this theory. See I said the word test, There it is. And let's look at the definition of this site. Hey, look at that, it takes in a type or M, it intersects it with empty object, there you go.

[00:11:34] So I promised we'd cover a bunch of utility types, you need to understand how they work. There you go. Sometimes it's useful to intersect types to just remove a piece of that type. That's these almost top types, sometimes it's useful. Look what happens here. Who thinks they know what the result is gonna be here?

[00:11:59] What does intersecting with any do? This is safe, sorry, we went to any, it's not the same. We intersected with any and so now we're at any. That's confusing.
>> [LAUGH]
>> All right, we're just gonna move on here.
>> [LAUGH]
>> This feels like we're going down the rabbit hole, but it was any anyways, was it?

[00:12:29] This was not any, I have an explanation here for you. When we intersected with empty object type, we removed the things that were incompatible with the empty object type. Here, we're removing the things that are incompatible with any, and there's nothing there, right? There's nothing to remove. I kind of feel like I would prefer to see string or number on null or undefined.

[00:12:57] But you could think of it that way. I kind of expected this to be a no op and instead it's a, any kind of consumes everything there. Anyway, we don't have to understand that, we'll move on. It broke my too, so don't worry.