430

I am creating an array of objects in TypeScript:

 userTestStatus xxxx = {
    "0": { "id": 0, "name": "Available" },
    "1": { "id": 1, "name": "Ready" },
    "2": { "id": 2, "name": "Started" }
 };

Can someone tell me how I could declare its type correctly? Is it possible to do inline or would I need two definitions?

I'm looking to replace the xxx with a type declaration, so that later on TypeScript would alert me if I used something like userTestStatus[3].nammme by mistake.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129

10 Answers10

590

You are better off using a native array instead of an object literal with number-like properties, so that numbering (as well as numerous other array functions) are taken care of off-the-shelf.

What you are looking for here is an inline interface definition for your array that defines every element in that array, whether initially present or introduced later:

let userTestStatus: { id: number, name: string }[] = [
    { "id": 0, "name": "Available" },
    { "id": 1, "name": "Ready" },
    { "id": 2, "name": "Started" }
];

userTestStatus[34978].nammme; // Error: Property 'nammme' does not exist on type [...]

If you are initializing your array with values right away, the explicit type definition is not a necessity; TypeScript can automatically infer most element types from the initial assignment:

let userTestStatus = [
    { "id": 0, "name": "Available" },
    ...
];

userTestStatus[34978].nammme; // Error: Property 'nammme' does not exist on type [...]
AmerO
  • 5
  • 2
John Weisz
  • 30,137
  • 13
  • 89
  • 132
152

What you have above is an object, not an array.

To make an array use [ & ] to surround your objects.

userTestStatus = [
  { "id": 0, "name": "Available" },
  { "id": 1, "name": "Ready" },
  { "id": 2, "name": "Started" }
];

Aside from that TypeScript is a superset of JavaScript so whatever is valid JavaScript will be valid TypeScript so no other changes are needed.

Feedback clarification from OP... in need of a definition for the model posted

You can use the types defined here to represent your object model:

type MyType = {
    id: number;
    name: string;
}

type MyGroupType = {
    [key:string]: MyType;
}

var obj: MyGroupType = {
    "0": { "id": 0, "name": "Available" },
    "1": { "id": 1, "name": "Ready" },
    "2": { "id": 2, "name": "Started" }
};
// or if you make it an array
var arr: MyType[] = [
    { "id": 0, "name": "Available" },
    { "id": 1, "name": "Ready" },
    { "id": 2, "name": "Started" }
];
Brocco
  • 62,737
  • 12
  • 70
  • 76
  • Sorry maybe my question was not clear. What I wanted to do was to find a definition for userTestStatus. –  Feb 16 '16 at 14:29
  • 1
    @Marilou You can usually get the definition simply by hovering over the `userTestStatus` variable in your favourite IDE... the TypeScript playground shows `{ id: number, name: string; }[]`. You could wrap that in an interface if you like `interface NameThis { id: number, name: string; }` and `NameThis[]` as the array type. – Fenton Feb 16 '16 at 14:40
  • I tried this but it gives me a few errors. However I think it's something like I want: userTestStatus: { id: number, name: string; }[] = { "0": { "id": 0, "name": "Available" }, "1": { "id": 1, "name": "Ready" }, –  Feb 16 '16 at 14:51
  • In your example defining `var arr: MyType`, you wouldn't use the property name/index declarations such as `"0": { … }`; you would just use the object literal at this point. – gfullam Feb 16 '16 at 14:55
  • @gfullam you are correct, I updated my answer (it was a quick careless edit) – Brocco Feb 16 '16 at 14:57
  • @Brocco - Your type MyGroupType = { looks something like I need but what I gave here is just an example. I have many different statuses. Do I need to define each ? –  Feb 16 '16 at 15:07
  • @Marilou No, I generalized it above in my answer – Brocco Feb 16 '16 at 15:10
  • Arrays in Javascript are actually objects behind the hood. The only difference is that the keys are "0", "1" etc. The Object the user send, in javascript is an array. To verify you type in node typeof([1,2,3]) – Ben Carp Oct 22 '18 at 13:41
  • @Brocco, what's the difference between `type` and `interface` seems like they are doing the same thing. – Alexander Kim May 26 '19 at 06:25
75

Some tslint rules are disabling use of [], example message: Array type using 'T[]' is forbidden for non-simple types. Use 'Array<T>' instead.

Then you would write it like:

var userTestStatus: Array<{ id: number, name: string }> = Array(
    { "id": 0, "name": "Available" },
    { "id": 1, "name": "Ready" },
    { "id": 2, "name": "Started" }
);
Ogglas
  • 62,132
  • 37
  • 328
  • 418
68

Array<T>

person: Array<{
  name: string;
  age: number;
}>
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
www
  • 689
  • 4
  • 3
27

What you really want may simply be an enumeration

If you're looking for something that behaves like an enumeration (because I see you are defining an object and attaching a sequential ID 0, 1, 2 and contains a name field that you don't want to misspell (e.g. name vs naaame), you're better off defining an enumeration because the sequential ID is taken care of automatically, and provides type verification for you out of the box.

enum TestStatus {
    Available,     // 0
    Ready,         // 1
    Started,       // 2
}

class Test {
    status: TestStatus
}

var test = new Test();
test.status = TestStatus.Available; // type and spelling is checked for you,
                                    // and the sequence ID is automatic

The values above will be automatically mapped, e.g. "0" for "Available", and you can access them using TestStatus.Available. And Typescript will enforce the type when you pass those around.

If you insist on defining a new type as an array of your custom type

You wanted an array of objects, (not exactly an object with keys "0", "1" and "2"), so let's define the type of the object, first, then a type of a containing array.

class TestStatus {
    id: number
    name: string

    constructor(id, name){
        this.id = id;
        this.name = name;
    }
}

type Statuses = Array<TestStatus>;

var statuses: Statuses = [
    new TestStatus(0, "Available"),
    new TestStatus(1, "Ready"),
    new TestStatus(2, "Started")
]
bakkal
  • 54,350
  • 12
  • 131
  • 107
  • Sorry maybe my question was not clear. What I wanted to do was to find a definition for userTestStatus so that Typescript would allow me to check where it is used and so for example I could not enter userTestStatus[1].naaaame. –  Feb 16 '16 at 14:29
  • I wanted to find something to put in between the "userTestStatus" and the "=". In the same way that I might put string or number there to say what type the variable has. Something like Brocco just did but with just one line if that is possible. –  Feb 16 '16 at 14:37
25

A little old but I felt I could add some clarity to this.

Exact Answer

    interface MyObject {
      id: number;
      name: string;
    }

    interface MyExactData {
      [key: string]: MyObject;
    }

    let userTestStatus: MyExactData = {
      "0": { "id": 0, "name": "Available" },
      "1": { "id": 1, "name": "Ready" },
      "2": { "id": 2, "name": "Started" }
    };

But the above is not how we usually do an array of objects, you would use the simple native array in javaScript.

    interface MyObject { // define the object (singular)
      id: number;
      name: string;
    }

    let userTestStatus_better: MyObject[] = [
      { "id": 0, "name": "Available" },
      { "id": 1, "name": "Ready" },
      { "id": 2, "name": "Started" }
    ];

Simply adding [] to our interface provides the typing for an array of said objects. And to do that inline

    let userTestStatus_inline: {id:number, name:string}[] = [
      { "id": 0, "name": "Available" },
      { "id": 1, "name": "Ready" },
      { "id": 2, "name": "Started" }
    ];

I would use the interface as you have something that is definable, understandable, and reusable. If you need to make changes, you can make changes to the one interface and typescript will report your interface to code mismatches.

Eddie
  • 457
  • 4
  • 7
3

You can also try

    interface IData{
        id: number;
        name:string;
    }

    let userTestStatus:Record<string,IData> = {
        "0": { "id": 0, "name": "Available" },
        "1": { "id": 1, "name": "Ready" },
        "2": { "id": 2, "name": "Started" }
    };

To check how record works: https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt

Here in our case Record is used to declare an object whose key will be a string and whose value will be of type IData so now it will provide us intellisense when we will try to access its property and will throw type error in case we will try something like userTestStatus[0].nameee

Akash Singh
  • 547
  • 5
  • 8
1
 var xxxx : { [key:number]: MyType };
0

When the satisfies operator was added in ts4.9, this was no longer an issue.

const xxxx = {
    "0": { "id": 0, "name": "Available" },
    "1": { "id": 1, "name": "Ready" },
    "2": { "id": 2, "name": "Started" }
 } satisfies { [k in string]: {id:number,name:string} };

xxxx[3] //  error

missannil
  • 137
  • 6
0

also you can pass like:

const xxxx = ( p1 : type, p2 : type) => [{}]
sadeq shahmoradi
  • 1,395
  • 1
  • 6
  • 22
  • There are **nine existing answers** to this question, including a top-voted, accepted answer with over **five hundred votes**. Are you _certain_ your solution hasn't already been given? If not, why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is _always_ useful on Stack Overflow, but it's _especially_ important where the question has been resolved to the satisfaction of both the OP and the community. Help readers out by explaining what your answer does different and when it might be preferred. – Jeremy Caney Aug 29 '23 at 05:31