Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typescript interface dynamic param does not compile without any

Tags:

typescript

I have a type as follows, where if name is anything OTHER THAN "filter" the type is "AggrEntry" and "filter" is of type "Aggr".

export interface Aggr {
    [name: string]: AggrEntry;
    filter?: Aggr;
}

However the ts code won't compile unless I change [name: string]: AggrEntry; to [name: string]: any;.

The error is

[ts] Property 'filter' of type 'ResponseAggregation' is not assignable to string index type 'ResponseAggregationEntry'.

Logically I am guessing typescript tries to assign [name: string] to filter since filter itself can get mapped to [name: string]. So how would I structure my interface so that the ts compiler knows that "name" won't ever be "filter" then.

like image 226
MilindaD Avatar asked Apr 03 '18 12:04

MilindaD


1 Answers

If you define an indexer all properties must conform to the return type of the interface. You could do the following:

export interface Aggr {
    [name: string]: AggrEntry |Aggr;
    filter?: Aggr;
}

This is obviously not ideal as this allows other fields except filter to be of type Aggr.

Another option is to use a type definition instead of an interface, and use a intersection type:

export type Aggr  = {
    [name: string]: AggrEntry;
} & {
    filter?: Aggr;
}

let test : Aggr;
let foo = test.foo // foo is AggrEntry
test.filter // works, is of type Aggr

While we can access object fields as expected, creating an object of this type is a bit trickier. Creating an object literal with the filter field will yield a similar error to your original. We can use Object.assign to create an instance of the type using object literals:

let test : Aggr = Object.assign({
    foo: new AggrEntry()
}, {
    filter: {
        bar: new AggrEntry()
    }
});

Or we can create a dedicated function to help with creation, that uses Object.assign:

function createAggr(dynamicPart: {
    [name: string]: AggrEntry;
}, staticPart?: {
    filter?: Aggr;
}) {
    return Object.assign(dynamicPart, staticPart);
}

let test : Aggr = createAggr({
    foo: new AggrEntry()
}, {
    filter: {
        bar: new AggrEntry()
    }
});
like image 104
Titian Cernicova-Dragomir Avatar answered Dec 08 '22 08:12

Titian Cernicova-Dragomir