241

I have the following interface and code. I thought I was doing the definitions correctly but I am getting an error:

interface IenumServiceGetOrderBy { id: number; label: string; key: any }[];

and:

getOrderBy = (entity): IenumServiceGetOrderBy => {
        var result: IenumServiceGetOrderBy;
        switch (entity) {
            case "content":
                result =
                [
                    { id: 0, label: 'CId', key: 'contentId' },
                    { id: 1, label: 'Modified By', key: 'modifiedBy' },
                    { id: 2, label: 'Modified Date', key: 'modified' },
                    { id: 3, label: 'Status', key: 'contentStatusId' },
                    { id: 4, label: 'Status > Type', key: ['contentStatusId', 'contentTypeId'] },
                    { id: 5, label: 'Title', key: 'title' },
                    { id: 6, label: 'Type', key: 'contentTypeId' },
                    { id: 7, label: 'Type > Status', key: ['contentTypeId', 'contentStatusId'] }
                ];
                break;
        }
        return result;
    };

Error:

Error   190 Cannot convert '{}[]' to 'IenumServiceGetOrderBy':
    Type '{}[]' is missing property 'id' from type 'IenumServiceGetOrderBy'
Flip
  • 6,233
  • 7
  • 46
  • 75

15 Answers15

374

You don't need to use an indexer (since it a bit less typesafe). You have two options :

interface EnumServiceItem {
    id: number; label: string; key: any
}

interface EnumServiceItems extends Array<EnumServiceItem>{}


// Option A 
var result: EnumServiceItem[] = [
    { id: 0, label: 'CId', key: 'contentId' },
    { id: 1, label: 'Modified By', key: 'modifiedBy' },
    { id: 2, label: 'Modified Date', key: 'modified' },
    { id: 3, label: 'Status', key: 'contentStatusId' },
    { id: 4, label: 'Status > Type', key: ['contentStatusId', 'contentTypeId'] },
    { id: 5, label: 'Title', key: 'title' },
    { id: 6, label: 'Type', key: 'contentTypeId' },
    { id: 7, label: 'Type > Status', key: ['contentTypeId', 'contentStatusId'] }
];

// Option B
var result: EnumServiceItems = [
    { id: 0, label: 'CId', key: 'contentId' },
    { id: 1, label: 'Modified By', key: 'modifiedBy' },
    { id: 2, label: 'Modified Date', key: 'modified' },
    { id: 3, label: 'Status', key: 'contentStatusId' },
    { id: 4, label: 'Status > Type', key: ['contentStatusId', 'contentTypeId'] },
    { id: 5, label: 'Title', key: 'title' },
    { id: 6, label: 'Type', key: 'contentTypeId' },
    { id: 7, label: 'Type > Status', key: ['contentTypeId', 'contentStatusId'] }
]

Personally I recommend Option A (simpler migration when you are using classes not interfaces).

basarat
  • 261,912
  • 58
  • 460
  • 511
  • 4
    Is adding `?` to all the declarations in the interface, eg. `id?: number; label?: string; key?: any` the best way to avoid a casting issue if you use _less_ properties than what is defined in the interface? Ie. I may not specify `key` in the typed array setup. – GONeale Feb 01 '16 at 04:48
  • Option B is useful since it allows you to use the interface while composing higher level interfaces. – aashtonk Oct 18 '16 at 15:33
  • 7
    The second interface declaration gives the following tslint error: "*an interface declaring no members is equivalent to its supertype*" – Inigo Jul 21 '17 at 14:48
  • 2
    May I suggest that 'key' is of type string | string[] instead of any. Like this: `interface EnumServiceItem { id: number; label: string; key: string | string[] }` – JulianG Dec 06 '17 at 13:27
  • 1
    TSLint autocorrects this into `type EnumServiceItems = Array` and avoids the error mentioned by @Inigo – Ivan P Aug 05 '20 at 17:52
161

You can define an interface with an indexer:

interface EnumServiceGetOrderBy {
    [index: number]: { id: number; label: string; key: any };
}
Dane Brouwer
  • 2,827
  • 1
  • 22
  • 30
Douglas
  • 36,802
  • 9
  • 76
  • 89
  • 62
    Not really an "interface for an array of objects with Typescript" – basarat Aug 24 '14 at 10:35
  • 2
    Yes, your example of defining an interface only for the particular items would be a more useful way to do it. It would be quite rare to have an array of items, but not want to conveniently reference a single item. Using a real array also exposes .length on the interface, which will probably be used quite often. – Douglas Aug 24 '14 at 12:50
  • How to define class to impliment this interface? – alexey May 19 '16 at 10:56
  • 42
    The problem with this solution is this is not an `Array` interface, therefore all `Array` properties and methods such as `length` or `push` will be missing. – Norman Breau Dec 10 '17 at 07:54
  • 1
    With this you will end up in error while using array functions see this - https://stackoverflow.com/q/50215823/9640177 – Mayank Kumar Chaudhari Jan 24 '21 at 05:46
  • works very well for me – C Alonso C Ortega Dec 17 '21 at 00:24
142

You can define an interface as array with simply extending the Array interface.

export interface MyInterface extends Array<MyType> { }

With this, any object which implements the MyInterface will need to implement all function calls of arrays and only will be able to store objects with the MyType type.

NoNameProvided
  • 8,608
  • 9
  • 40
  • 68
  • 4
    Why not to use a type (`type MyTypeArray = Array`) instead of extending an interface? – Pau Fracés Apr 06 '18 at 10:21
  • 1
    OP asked for interface, I assume it is extended with other properties and methods, you cannot do that with a type and also type is more limited than an interfaces in terms of general usability – NoNameProvided Apr 06 '18 at 10:27
  • @NoNameProvided "type is more limited than an interfaces in terms of general usability" Can you provide more detail on this statement? What are the limitations of using types versus interfaces? – Pau Fracés Apr 06 '18 at 11:35
  • 2
    I look at this and I wish that `MyType` was also defined in this example. I can ALMOST use this answer, except, of course, I get stuck because I don't know what `MyType` is supposed to look like. Would it be possible to update the answer with that? – Will Lanni Apr 11 '18 at 16:36
  • It is an interface or a built in type like string or number – NoNameProvided Apr 11 '18 at 16:53
  • Coming from a C# background I instinctively used `:` then came the syntax errors that ultimately led me to this post +1. – Phil Cooper Oct 29 '18 at 09:07
  • `MyType[]` will also work as an alternative if you don't need it as it's own specific interface – Emobe May 08 '19 at 19:26
  • 1
    @PauFracés now even Microsoft discourages the usage of types and instead advice to use interfaces: https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections – NoNameProvided Nov 24 '20 at 15:59
  • Why are the {} needed at the end of the type declaration? If I want an array of objects type, isn't it sufficient to declare MyType as an object? – Flip Sep 25 '21 at 10:29
  • `{}` is required because it is an interface. I assume OP wanted to extend the array interface. Also don't use types when you can use interfaces says the official TS docs. – NoNameProvided Sep 25 '21 at 11:33
  • This results for me in eslint error: An interface declaring no members is equivalent to its supertype. – Eggon Jul 10 '23 at 13:47
78

Additional easy option:

    interface simpleInt {
        id: number;
        label: string;
        key: any;
    }

    type simpleType = simpleInt[];
Dane Brouwer
  • 2,827
  • 1
  • 22
  • 30
33

Do not use

interface EnumServiceGetOrderBy {
    [index: number]: { id: number; label: string; key: any };
}

You will get errors for all the Arrays properties and methods such as splice etc.

The solution is to create an interface that defines an array of another interface (which will define the object)

For example:

interface TopCategoriesProps {
  data: Array<Type>;
}
interface Type {
  category: string;
  percentage: number;
}
Jeremy Levett
  • 939
  • 9
  • 13
19

So I'll add my two cents :)

Let's say you want a Workplace type full of Persons. You need something like this:

Wrong! ... as interface


interface Workplace {
   name: string
   age: number
}[] 
// that [] doesn't work!! ;)

The problem here is that interface is for specifiing Class/Object shape .. not much more. So you may use type instead witch is much more flexible

Better -> use type instead of interface


type Workplace = {
   name: string
   age: number
}[] 

// This works :D

But maybe best is ...

define inner object Person with interface and then Workplace as type made by array of persons -> Person[]


interface Person {
   name: string
   age: number
}

type Workplace = Person[]

// nice ;)

Good luck

JsonKody
  • 570
  • 3
  • 13
18

Use like this!

interface Iinput {
  label: string
  placeholder: string
  register: any
  type?: string
  required: boolean
}


// This is how it can be done

const inputs: Array<Iinput> = [
  {
    label: "Title",
    placeholder: "Bought something",
    register: register,
    required: true,
  },
]
Ericgit
  • 6,089
  • 2
  • 42
  • 53
8

Here's an inline version if you don't want to create a whole new type:

export interface ISomeInterface extends Array<{
    [someindex: string]: number;
}> { };
CiriousJoker
  • 552
  • 1
  • 7
  • 18
8

In Angular use 'extends' to define the interface for an 'Array' of objects.

Using an indexer will give you an error as its not an Array interface so doesn't contain the properties and methods.

e.g

error TS2339: Property 'find' does not exist on type 'ISelectOptions2'.

// good    
export interface ISelectOptions1 extends Array<ISelectOption> {}

// bad
export interface ISelectOptions2 {
    [index: number]: ISelectOption;
}

interface ISelectOption {
    prop1: string;
    prop2?: boolean;
}
Graham Mills
  • 91
  • 1
  • 4
  • This method will result in "An interface declaring no members is equivalent to its supertype" if the linter doesn't allow empty interfaces. – demisx Dec 26 '19 at 20:37
  • This answer was soooo close it helped me solve my problem! For the initial object for which you want to create an array within another interface, you type "interface ISelectOptions { name1 : type; name2: type; } then within your BIG interface, when you get to the key you want to be an array of that type, keyZ: ISelectOptions[]; then when you create the JSON, myVar : BIG = {key1: val1, key2: val2, …, keyZ: [{name1 : valA, name2: valB}, {name1 : valC, name2: valD}] worked like a champ! (didn't need to extend the Array class, though) Thanks! – AppDreamer Jan 09 '20 at 16:06
5

Easy option with no tslint errors ...

export interface MyItem {
    id: number
    name: string
}

export type MyItemList = [MyItem]
Euan Millar
  • 456
  • 1
  • 6
  • 13
4

Also you can do this.

            interface IenumServiceGetOrderBy {
                id: number; 
                label: string; 
                key: any;
            }

            // notice i am not using the []
            var oneResult: IenumServiceGetOrderBy = { id: 0, label: 'CId', key: 'contentId'};

            //notice i am using []
            // it is read like "array of IenumServiceGetOrderBy"
            var ArrayOfResult: IenumServiceGetOrderBy[] = 
            [
                { id: 0, label: 'CId', key: 'contentId' },
                { id: 1, label: 'Modified By', key: 'modifiedBy' },
                { id: 2, label: 'Modified Date', key: 'modified' },
                { id: 3, label: 'Status', key: 'contentStatusId' },
                { id: 4, label: 'Status > Type', key: ['contentStatusId', 'contentTypeId'] },
                { id: 5, label: 'Title', key: 'title' },
                { id: 6, label: 'Type', key: 'contentTypeId' },
                { id: 7, label: 'Type > Status', key: ['contentTypeId', 'contentStatusId'] }
            ];
Rolly
  • 3,205
  • 3
  • 26
  • 35
2

You can define a type as an array of objects by simply extending the interface. Here's an example below :

// type of each item in the Service list
interface EnumServiceItem {
    id: string;
    label: string;
}

// type of the Service 
interface ServiceType {
    id: string,
    label: string,
    childList?: Array<EnumServiceItem>
}

// type of the Service list
type ServiceListType = Array<ServiceType>

let draggableList:ServiceListType =  [
        {
            id: "1",
            label: 'Parent Item 1',
            childList: [
                {
                    id: "11",
                    label: 'Child Item 1',
                },
                {
                    id: "12",
                    label: 'Child Item 2',
                }
                ,
                {
                    id: "13",
                    label: 'Child Item 3',
                }
            ]
        },
        {
            id: "2",
            label: 'Parent Item 2',
            childList: [
                {
                    id: "14",
                    label: 'Child Item 4',
                },
                {
                    id: "15",
                    label: 'Child Item 5',
                }
                ,
                {
                    id: "16",
                    label: 'Child Item 6',
                }
            ]
        },
        {
            id: "3",
            label: 'Parent Item 3',
            childList: [
                {
                    id: "17",
                    label: 'Child Item 7',
                },
                {
                    id: "18",
                    label: 'Child Item 8',
                }
                ,
                {
                    id: "19",
                    label: 'Child Item 9',
                }
            ]
        },

    ]
Jakub Kurdziel
  • 3,216
  • 2
  • 12
  • 22
1

Programming is simple. Use simple usecase:

interface IenumServiceGetOrderBy { id: number; label: string; key: any }
 // OR
interface IenumServiceGetOrderBy { id: number; label: string; key: string | string[] }

// use interface like
const result: IenumServiceGetOrderBy[] = 
                [
                    { id: 0, label: 'CId', key: 'contentId' },
                    { id: 1, label: 'Modified By', key: 'modifiedBy' },
                    { id: 4, label: 'Status > Type', key: ['contentStatusId', 'contentTypeId'] }
                ];


Rahmat Ali
  • 1,430
  • 2
  • 17
  • 29
1

Here is one solution adapted to your example:

interface IenumServiceGetOrderByAttributes { 
  id: number; 
  label: string; 
  key: any 
}

interface IenumServiceGetOrderBy extends Array<IenumServiceGetOrderByAttributes> {

}

let result: IenumServiceGetOrderBy;

With this solution you can use all properties and methods of the Array (like: length, push(), pop(), splice() ...)

Amel
  • 653
  • 9
  • 15
1

I would use the following structure:

interface arrayOfObjects extends Array<{}> {}

And then it's easier to define:

let myArrayOfObjects: arrayOfObjects = [
  { id: 0, label: "CId", key: "contentId" },
  { id: 1, label: "Modified By", key: "modifiedBy" },
  { id: 2, label: "Modified Date", key: "modified" },
  { id: 3, label: "Status", key: "contentStatusId" },
  { id: 4, label: "Status > Type", key: ["contentStatusId", "contentTypeId"] },
  { id: 5, label: "Title", key: "title" },
  { id: 6, label: "Type", key: "contentTypeId" },
  { id: 7, label: "Type > Status", key: ["contentTypeId", "contentStatusId"] },
];