Party Ark
Party Ark

Reputation: 1159

Typescript - Infer strongly-typed recursive index type

Starting with an Animal interface, and a map of animals in my farm:

export interface Animal {
   type: string;
   edible: boolean;
}
export interface Farmland{
   [key: string]: Animal;
}

All works good. Here's my farm -

const farm: Farmland = {

  bob: { type: 'sheep', edible: true },
  daisy: { type: 'cow', edible: true }

}

Now my farn might have some smaller parts in it, and I want a new type which will contain either the animals or these parts:

export interface FarmlandOrAnimalMap {
  [key: string]: Animal | Farmland;
}

const bigField: FarmlandOrAnimalMap = {

  bob: { type: 'sheep', edible: true },
  daisy: { type: 'cow', edible: true },

  pasture: {
    jack: { type: 'dog', edible: false }
  },
  pond: {
    donald: { type: 'duck', edible: true }
  },
  farmhouse: {
    hernietta: { type: 'monkey', edible: false }
  }
};

That seems to work too - I can happily reference {{bigField.pond.donald.edible}} (using e.g. angular) and all other expected object properties in my markup. Here's what I can't do though -

export class AnimalOven{

  constructor() {

    const donald = bigField.pond.donald;

  }
}

The error is Property 'donald' does not exist on type 'Animal | Farmland'. Property 'donald' does not exist on type 'Animal' . (This error will also be thrown by an angular compiler when I build the application.)

Typescript evidently knows bigField.pond might be Farmland (with its string index) or an Animal so I'm not sure why it cannot infer the correct type here. In short:

Is there a way for Typescript to correctly infer type when using a type union of strong-type and {[key:index]: strong-type} ?

(Obviously there might be better ways of creating my farm and animals, use of keys, appropriate use of arrays; but the question here really relates to Typescript inference of map types)

Thanks.

Upvotes: 0

Views: 286

Answers (1)

brunnerh
brunnerh

Reputation: 185280

By declaring it to be a FarmlandOrAnimalMap you tell the compiler that the content of the variable is more generic than what it actually is here.

If you take away the type annotation of bigField, this will work as the type of the entire object tree is derived.

For any variable of type FarmlandOrAnimalMap the compiler can never infer whether a property is an Animal or Farmland unless you introduce additional checks.

Upvotes: 0

Related Questions