Property 'id' does not exist on type '{}'.
Tags: typescript, tip, javascript
Published: 2020-05-18 10:03:20

<small><em>(Photo by Daniel Jensen on Unsplash)</em></small>

_This article was first published on [my blog](https://words.mjsarfatti.com/typescript-cheats:-property-'id'-does-not-exist-on-type-''.)🔗._

**TL;DR**:

Either:

```ts
const isValidObject = (myObject as ValidObject).id !== undefined;
```

Or, better, define a type guard:

```ts
function isValidObject(myObject: ValidObject | {}): myObject is ValidObject {
  return (myObject as ValidObject).id !== undefined;
}
```

---

_I'm publishing this tip mainly because it's the third time I run into this problem, and the third time I get lost on the internet trying to understand what it is I'm doing wrong. I hope next time I search for this, this post will come up! Read on if you want a better understandind of what the cheat-sheet code above does and where it comes from:_

When writing regular JavaScript we are used to a certain degree of flexibility when it comes to objects. Take the following example:

```js
// Imaginary call to an API that returns the venue with ID 1,
// or an empty object if there is no venue with that ID
const venue = getVenue(1);

// Let's check if a venue was found by verifying the existence of  the `id` property
const weHaveVenue = venue.id !== undefined;

if (weHaveVenue) {
  // do something
} else {
  // do something else...
}
```

Pretty straightforward, right?

Well, the moment we use TypeScript, things don't work so smoothly anymore. Have a look a this implementation:

```ts
// Let's define the type of our imaginary API function first
type GetVenue = (
  id: number
) => { id: number; name: string; location: string } | {};

// And then write a sample (and NOT real world production code) implementation
// faking an API call that might or might not find (and return) a venue
const getVenue: GetVenue = function(id) {
  const state = id < 10 ? 200 : 404;

  return state === 200
    ? {
        id,
        name: "Meetings Central",
        location: "North Pole",
      }
    : {};
};

const venue = getVenue(1);

const weHaveVenue = venue.id !== undefined; // ❌ Property 'id' does not exist on type '{}'.

if (weHaveVenue) {
  // do something
} else {
  // do something else...
}
```

I know what you are thinking: "Wait, I _know_ that. That's exactly why I'm checking on the id!". But TypeScript needs a little more holding hands:

```ts
// Let's define two more types since we will have to reuse them in our code
type Venue = { id: number; name: string; location: string };
type NoVenue = {};

type GetVenue = (id: number) => Venue | NoVenue;

const getVenue: GetVenue = function(id) {
  const state = id < 10 ? 200 : 404;

  return state === 200
    ? {
        id,
        name: "Meetings Central",
        location: "North Pole",
      }
    : {};
};

const venue = getVenue(1);

// By casting our `venue` to a `Venue` type, and then checking on the `id` property,
// we are basically telling TypeScript: "trust us, at runtime we're gonna be fine"
const weHaveVenue = (venue as Venue).id !== undefined; // ✅

if (weHaveVenue) {
  // do something
} else {
  // do something else...
}
```

Hurrah 🙌

This may (and will) work well in several, simple cases. But what if further down we also want to use that `venue` object? Let's say we need an upper-cased version of the venue name, and add one line of code to our if/else statement:

```ts
[...]

if (weHaveVenue) {
  // do something with our venue object
  const upperName = venue.name.toUpperCase(); // ❌ Property 'name' does not exist on type 'NoVenue'.
} else {
  // do something else...
}
```

Whoops 😕. Back at square one.

In this case we need to move our check in a custom _type guard_, which is fancy wording "a function that checks a type". Check out the full code:

```ts
type Venue = { id: number; name: string; location: string };
type NoVenue = {};
type GetVenue = (id: number) => Venue | NoVenue;

// We move our id check into a function whose return type is "value is Type"
function isVenue(venue: Venue | NoVenue): venue is Venue {
  return (venue as Venue).id !== undefined;
}

const getVenue: GetVenue = function(id) {
  const state = id < 10 ? 200 : 404;

  return state === 200
    ? {
        id,
        name: "Meetings Central",
        location: "North Pole",
      }
    : {};
};

const venue = getVenue(1);

// We can now call our type guard to be sure we are dealing with one type, and not the other
if (isVenue(venue)) {
  // do something with our venue object
  const upperName = venue.name.toUpperCase(); // ✅
} else {
  // do something else...
}
```

To paraphrase the official [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types):

> Any time `isVenue` is called with some variable, TypeScript will narrow that variable to that specific type if the original type is compatible.

---

This brief excursion should've clarified a feature of TypeScript that may leave someone coming from JavaScript perplexed. At least, it troubled me a few times! I'd love to hear your comments: let's be friends on Twitter ([@mjsarfatti](https://twitter.com/mjsarfatti), DMs are open) and on [dev.to](https://dev.to/mjsarfatti).

If you'd like to be notified of the next article, please do subscribe to my [email list](https://buttondown.email/mjsarfatti). No spam ever, cancel anytime, and never more than one email per week (actually probably much fewer).