1
votes

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'.

1

1 Answers

2
votes

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)