Reputation: 58662
How to express an interface (IResponse), with one property has a string key (which is not known statically). Below, the key values
can be anything like books
, chairs
, etc. All other keys and types are known statically. Below implementation gives error. I guess the error is because the index signature on the IResponse
makes all the property values to be IValue[]
. Is there a way to do this?
export interface IMeta{}
export interface IValue{}
export interface IResponse {
meta: IMeta;
[index: string]:IValue[];
}
export class Response implements IResponse {
meta:IMeta;
values:IValue[];
//books:IValue[];
//anything:IValue[];
}
Upvotes: 15
Views: 28571
Reputation: 2210
Old question, but here is how I solved the issue for myself.
export interface Foo {
[key: string]: any;
}
{ something: 0 } as Foo => valid
{ other: 'hello' } as Foo => valid
Edit: January 14, 2023: There is a better way to do this for type safety if you know what the possible shapes are:
type ApiResponse<Type> = {
[Property in keyof Type]: Type[Property];
};
type BookResponse = { title: string; }
const myBookResponse: ApiResponse<BookResponse> = { title: 'foo' } // OK
const myBookResponse: ApiResponse<BookResponse> = { title: 42 } // ERROR
const myBookResponse: ApiResponse<BookResponse> = { title: 'foo', taco: true } // ERROR
Upvotes: 43
Reputation: 718
There seem to be no exact replies unfortunately, only approximations of a solution.
The solution is to use intersection type over two interfaces, one that defines static metadata and one that defines dynamic keys:
interface IStatic {
staticflag: boolean;
}
interface IDynamic {
[x: string]: string | number;
}
type Complex = IStatic & IDynamic;
const a: Complex = {
staticflag: false,
dynamic1: 'a',
};
Upvotes: -2
Reputation: 1926
Without interface:
const myFunc = ((myObjectWithSomeKeys: { [key: string]: any }) => {
});
Upvotes: 2
Reputation: 250812
If you define one interface for the known types and a separate one for the "unknown" types, you could use a type assertion to tell the compiler the one you wanted to use.
It isn't ideal, but you are working in a edge case (i.e. not entirely dynamic, not entirely static).
export interface IMeta{}
export interface IValue{}
export interface IFunkyResponse {
[index: string]:IValue[];
}
export interface IResponse {
meta: IMeta;
}
export class Response implements IResponse {
meta:IMeta;
values:IValue[];
books:IValue[];
anything:IValue[];
}
You can type-assert between <IResponse> resp
and <IFunkyResponse> resp
to access one or the other of the styles.
Upvotes: 13