Objects 16 exercises

Understanding Distributive Omit and Pick in TypeScript

Let's look at a common gotcha encountered with the Omit helper type. Similar quirks can also be observed with the Pick type, but for the sake of this discussion we'll keep our analysis to just Omit.

We start with three interface types: User, Organisation, and Product.

These types share commo

Loading explainer


00:00 In this explainer, I want to show you just a really common gotcha with both emit and pick, and we're going to just look at emit, but pick works in exactly the same way. So we have three types here. We have a user type, we have an organization type, and then we have a product

00:15 type down the bottom. And these are pretty similar types. They all have an ID, a name, and an image ID, but they have one property difference. So they have an age, which is different, address, which is different, and price, which is different on each of them. So we have a union down the bottom, which is an entity, which is user or organization or product.

00:35 And then we have an entity without ID. And we're expecting the entity without ID to basically be sort of the same structure as an entity, but with the ID property removed. So we'd expect to see a union of three things, basically the user, the organization, and the product there.

00:54 But we're not. We're actually just seeing a very simple object, which is just a single object with just name and ID, just the properties in common. Okay, that's really odd. So the reason this is happening is for a complicated reason. And I'm going to use a complicated word, which is emit

01:14 is not distributive. And distributive means when it gets past a union, it doesn't call itself, it doesn't iterate over each member of the union. It basically just mushes the union into a type that it can understand. And then does it, like, does it, what am I saying, like does the thing

01:33 on that type that it's mushed together. So in this case, it's mushed together. If I just like omit like something nonsensical from here, the thing that it's understanding the entity to be is something with ID, name, and image ID, because these are the keys that it's being

01:49 passed, right? And those are the ones in common between product, organization, and user. So this is really grim. And it means that you can get caught out using this really, really easily because omit is not behaving as you expect. In here, I have a definition for omit, which is

02:08 called a distributive omit. What this does is it basically has the same type as omit itself, but it calls itself on each member that's passed to it. So now entity without ID, if we hover over it, you can see it says omit user ID, omit organization ID, or omit product ID. And this

02:25 now behaves exactly as we want it to. This corresponds to this big old union down here with IDs removed. So this distributive omit and distributive pick too are extremely useful and worth sort of bringing into your project and into your utility types, because omit and pick can

02:43 sometimes catch you out here. And I've had situations where even when I was deep into some really complicated TypeScript code, I just needed to use a distributive omit instead of an omit or a distributive pick instead of a pick. So worth knowing about. And if you run into situations

02:58 where omit is squishing your union of objects together, you might consider using a distributive omit instead.