Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a TypeScript interface with both an index signature and a fixed property of a different type?

From a legacy api I am getting a JSON response like so:

const someObject = {
    "general": {
        "2000": 50,
        "4000": 100,
        "8000": 200,
    },
    "foo": [
        0,
        1,
        2,
    ],
    "bar": [
        5,
        7,
    ],
    "baz": [
        8,
        9,
    ],
};

Keep in mind that all the indexes except "general" are dynamic and might not be in the response, I cannot type for each property but have to use an index signature.

I wanted to achieve that via [email protected]:

interface ISomeObject {
    general: {
        [index: string]: number;
    };

    [index: string]?: number[];
}

as general will always be in the response, yet the other indexes might or might not be in there.

Issue that I am facing:

  • I cannot make the [index: string]?: number[] optional as it will complain that number is used as a value here.
  • [index: string]: number[] will override the definition of general: number and hence tsc will complain:

    Property 'general' of type '{ [index: string]: number; }' is not assignable to string index type 'number[]'.`
    

Can I even type for this format with a TypeScript interface?

like image 899
k0pernikus Avatar asked Nov 29 '18 09:11

k0pernikus


1 Answers

This is a variation of the TypeScript Dictarray concept.

The cheat-fix is to tell TypeScript that everything is fine and you know what you're doing:

interface ISomeObject {
    [index: string]: number[];
    // @ts-ignore: I'm creating a Dictarray!
    general: {
        [index: string]: number;
    };
}

The compiler will correctly infer the return types, which are numbers in this case:

let x: ISomeObject;

const a = x.general['idx'];
const b = x['idx'];

The linked article has more information, but this is the gist of it in your specific case.

like image 122
Fenton Avatar answered Oct 03 '22 19:10

Fenton