Corey Ogburn
Corey Ogburn

Reputation: 24717

Define interface as any but with guaranteed properties?

I'm hitting an API that returns a list of images in the following structure:

{
  "photos": {
    "1": "PHOTOS/1.jpg",
    "3": "PHOTOS/3.jpg",
    "4": "PHOTOS/4.jpg",
    "primary": "PHOTOS/X/PRI.jpg"
  },
  "sketches": {
    "1": "SKETCHES/X/1.jpg",
    "3": "SKETCHES/X/3.jpg",
    "4": "SKETCHES/X/4.jpg"
  }
}

The basic contract is that photos.primary always exists, all other properties on photos are numerical IDs, and for every ID in photos there's a matching ID in sketches. The IDs are not guaranteed to be continuous.

I would like to set up an interface called something like ImageIndex that knows that photos.primary is a field but also that any other field is acceptable. I know I could just mark photos and sketches as any but for the sake of intellisense I think it'd be worthwhile to define primary's existence.

I tried using an Intersection type like so:

export interface PropertyImageIndex {
  photos: ContainsPrimary&any;
  sketches: any;
}

interface ContainsPrimary {
  primary: string;
}

but when VSCode would normally show it's intellisense suggestions, there's nothing. This seems to indicate that any prevails over the explicit ImageList type of photos and I lose all suggestions.

Is there a way to tell typescript "this object has AT LEAST these fields, but also has any other fields I might ask it for" in a way that the guaranteed fields will show up as intellisense suggestions?

Upvotes: 1

Views: 67

Answers (1)

Nitzan Tomer
Nitzan Tomer

Reputation: 164129

If I understand you correctly:

interface Photos {
    primary: string;
    [name: string]: string;
}

interface PropertyImageIndex {
    photos: Photos;
    sketches?: { [name: string]: string };
}

let a: Photos = {}; // error
let b: Photos = { primary: "" };
let c: Photos = { primary: "", a: 3 }; // error
let d: Photos = { primary: "", a: "" };

let e: PropertyImageIndex = { photos: { primary: "" } };
let f: PropertyImageIndex = { photos: { primary: "" }, sketches: {} };
let g: PropertyImageIndex = { photos: { primary: "" }, sketches: { key: "" } };
let h: PropertyImageIndex = { photos: { primary: "" }, sketches: { key: 4 } }; // error

playground

Upvotes: 2

Related Questions