Search code examples
typescripttypestypescript-genericstypescript-eslint

Typescript error: Property 'a' does not exist on type 'A'


The below is an example of the types which I have

type Place = {
   address: string
}

type Location = {
   latLng: string
}

type User = {
  name: string
} & (Place | Location)

So when I try to use it while parsing the data

const { name, address, latLng } = user;

It shows a TS error Property 'address' does not exist on type 'User'

Technically I know that either address or latLng will be present but not both, which is how I need and my logic will validate it when parsing the data. But how to let TS know that about it and remove this error?


Solution

  • This is a great place to use never. This lets you set up two "configurations" of the User, which will have either a Place or a Loc. The Place type enforces an undefined latLng, while the Loc type enforces an undefined address. A User is one of ({name: string} & Place) | ({name: string} & Loc), so while all three fields exist in both types in the union, address or latLng will be undefined based on the presence of the other.

    type Place = {
      address: string;
      latLng?: never;
    };
    
    type Loc = {
      latLng: string;
      address?: never;
    };
    
    type User = {
      name: string;
    } & (Place | Loc);
    
    function test1() {
      // Valid, latLng is undefined
      const user: User = { name: "User", address: "1234 Somewhere St" };
      const { address, latLng, name } = user;
    }
    
    function test2() {
      // Valid, address is undefined
      const user: User = { name: "User", latLng: "123,456" };
      const { address, latLng, name } = user;
    }
    
    function test3() {
      // Invalid, both address and latLng are present
      const user: User = {
        name: "User",
        latLng: "123,456",
        address: "1234 Somewhere St",
      };
      const { address, latLng, name } = user;
    }
    

    (Playground link)

    This gives you a type which can have either a defined address or latLng but not both. You can destructure both values out of the user, but one or the other will be undefined.