1

I am unsure of the best way to add typescript typings for this response object I am receiving from a backend service:

{
    de49e137f2423457985ec6794536cd3c: {
        productId: 'de49e137f2423457985ec6794536cd3c',
        title: 'item 1',
    },
    d6623c1a2b843840b14c32685c212395: {
        productId: 'd6623c1a2b843840b14c32685c212395',
        title: 'item 2',
    },
    ids: [
        'de49e137f2423457985ec6794536cd3c',
        'd6623c1a2b843840b14c32685c212395',
    ],
}

It contains an array of item ids string[] as well as the index signature [id: string]: Item.

Typescript doesn't seem to like having the index signature as well as the array in a single interface. For example:

interface ItemList {
    [id: string]: Item;
    ids: string[];
}

I know that when using an index signature the other properties need to return the same type. I am new to Typescript and I'm a bit unsure as to how to work with this data without moving the ids out of the item object?

interface ItemList {
    [id: string]: Item;
    ids: string[];
}
interface Item {
    productId: string;
    title: string;
}

const item: ItemList = {
    de49e137f2423457985ec6794536cd3c: {
        productId: 'de49e137f2423457985ec6794536cd3c',
        title: 'item 1',
    },
    d6623c1a2b843840b14c32685c212395: {
        productId: 'd6623c1a2b843840b14c32685c212395',
        title: 'item 2',
    },
    ids: [
        'de49e137f2423457985ec6794536cd3c',
        'd6623c1a2b843840b14c32685c212395',
    ],
};
console.log(item.ids.map((id: string) => item[id]));

Error

Property 'map' does not exist on type 'Item | string[]'.

Property 'map' does not exist on type 'Item'.

AshJapan
  • 13
  • 4

1 Answers1

2

The simple fix here is to use an intersection type:

type ItemList = {
    [id: string]: Item;
} & {
    ids: string[];
}
interface Item {
    productId: string;
    title: string;
}

const item: ItemList = Object.assign({ // Can't build the object directly 
    de49e137f2423457985ec6794536cd3c: {
        productId: 'de49e137f2423457985ec6794536cd3c',
        title: 'item 1',
    },
    d6623c1a2b843840b14c32685c212395: {
        productId: 'd6623c1a2b843840b14c32685c212395',
        title: 'item 2',
    }
}, {
    ids: [
        'de49e137f2423457985ec6794536cd3c',
        'd6623c1a2b843840b14c32685c212395',
    ],
});
console.log(item.ids.map((id: string) => item[id]));

Intersection type allow the inconsistent, named property - index combination. (Note this is not strictly typesafe as item['ids'] does not return Item as expected, but it seems like a decent trade-off for this case)

Titian Cernicova-Dragomir
  • 157,784
  • 15
  • 245
  • 242
  • Titian, thank you for the quick response, this did the trick for me. Could you please explain the logic behind using Object.assign? When using an Intersection, do the two object types need to originate from separate objects matching the intersected types? Without it I receive the error: is not assignable to type 'ids & items.' – AshJapan Jun 26 '19 at 00:59