Question
I am creating a collection of objects in TypeScript like this:
const userTestStatus = {
"0": { id: 0, name: "Available" },
"1": { id: 1, name: "Ready" },
"2": { id: 2, name: "Started" }
};
How should I declare its type correctly?
Can this be done inline, or do I need to create separate type definitions?
I want to replace xxx with a proper type so that TypeScript can warn me about mistakes later, such as writing:
userTestStatus[3].nammme
instead of the correct property name.
Short Answer
By the end of this page, you will understand how to type collections of objects in TypeScript, especially the difference between a true array and an object with numeric keys. You will learn when to use inline types, type aliases, interfaces, arrays, tuples, and Record, and how these choices affect autocomplete and error checking.
Concept
In TypeScript, the first thing to notice is that your example is not actually an array. It is an object whose keys are strings that look like numbers:
const userTestStatus = {
"0": { id: 0, name: "Available" },
"1": { id: 1, name: "Ready" },
"2": { id: 2, name: "Started" }
};
A real array would look like this:
const userTestStatus = [
{ id: 0, name: "Available" },
{ id: 1, name: "Ready" },
{ id: 2, name: "Started" }
];
That difference matters because TypeScript uses different type systems for:
- arrays: ordered lists, accessed by index
- objects: key-value collections, accessed by property name or index signature
If your data is meant to be a list of statuses, an array is usually the best choice.
If your data is meant to be a lookup table keyed by IDs, then an object or may be better.
Mental Model
Think of this as choosing the right storage container:
- An array is like a row of boxes in order: box 0, box 1, box 2.
- An object is like a cabinet with labeled drawers: drawer
"0", drawer"1", drawer"2".
Both can store similar data, but they mean different things.
If you want “a list of statuses in order,” use an array. If you want “a dictionary of statuses by key,” use an object.
TypeScript then acts like a label checker:
- it checks whether each box contains the right shape
- it checks whether property names like
nameare spelled correctly - in some cases, it can even check whether a drawer key exists
Syntax and Examples
If you want a real array of objects, define a type for one object and then use that type in an array.
type Status = {
id: number;
name: string;
};
const userTestStatus: Status[] = [
{ id: 0, name: "Available" },
{ id: 1, name: "Ready" },
{ id: 2, name: "Started" }
];
This is the most common solution.
TypeScript will now allow:
userTestStatus[0].name
and flag:
userTestStatus[0].nammme
because nammme does not exist on Status.
Inline type
You can also write the type inline:
Step by Step Execution
Consider this example:
type Status = {
id: number;
name: string;
};
const userTestStatus: Status[] = [
{ id: 0, name: "Available" },
{ id: 1, name: "Ready" },
{ id: 2, name: "Started" }
];
const firstName = userTestStatus[0].name;
Step by step:
-
type Status = ...defines the shape of one status object.- every
Statusmust have:id: numbername: string
- every
-
const userTestStatus: Status[]means:userTestStatusis an array
Real World Use Cases
Typing arrays and object collections is common in almost every TypeScript codebase.
API responses
type User = {
id: number;
name: string;
};
const users: User[] = await fetchUsers();
This helps you safely access users[0].name.
Dropdown options
type Option = {
id: number;
label: string;
};
const options: Option[] = [
{ id: 1, label: "Small" },
{ id: 2, label: "Medium" },
{ id: 3, label: "Large" }
];
Status lookup tables
= {
: ;
: ;
};
: <, > = {
: { : , : },
: { : , : }
};
Real Codebase Usage
In real projects, developers usually separate two concerns:
- the type of one item
- the type of the collection
Example:
interface Status {
id: number;
name: string;
}
const statuses: Status[] = [
{ id: 0, name: "Available" },
{ id: 1, name: "Ready" }
];
This pattern makes code easier to reuse.
Common patterns
Validation before use
function printStatus(status: Status) {
console.log(status.name);
}
Array methods
Once your array is typed, methods like map, filter, and find become safer:
Common Mistakes
1. Calling an object an array
This is very common.
Broken idea:
const data = {
0: { id: 0, name: "Available" },
1: { id: 1, name: "Ready" }
};
This is an object, not an array.
A real array uses square brackets:
const data = [
{ id: 0, name: "Available" },
{ id: 1, name: "Ready" }
];
2. Repeating inline types everywhere
This works:
const a: { id: number; name: string }[] = [];
But repeating it many times makes code harder to maintain. Prefer a reusable type:
type Status = {
: ;
: ;
};
Comparisons
| Choice | Best for | Example | Notes |
|---|---|---|---|
Status[] | A normal list of items | const statuses: Status[] = [...] | Most common option |
| Inline array type | Small one-off arrays | const statuses: { id: number; name: string }[] = [...] | Fine for short examples |
Record<number, Status> | Lookup by numeric key | const statusMap: Record<number, Status> = { 0: ..., 1: ... } | Better for key-value access |
| Tuple | Fixed number of items in fixed positions | const statuses: [Status, Status, Status] = [...] | Stricter than a normal array |
Cheat Sheet
// One object type
type Status = {
id: number;
name: string;
};
// Array of objects
const list: Status[] = [
{ id: 0, name: "Available" }
];
// Inline array type
const list2: { id: number; name: string }[] = [
{ id: 1, name: "Ready" }
];
// Object lookup by numeric key
const map: Record<number, Status> = {
0: { id: 0, name: "Available" }
};
// Fixed-size tuple
const tuple: [Status, Status] = [
{ id: 0, name: "Available" },
{ id: , : }
];
FAQ
Is { "0": ..., "1": ... } an array in TypeScript?
No. That is an object with keys that look like numbers. A real array uses square brackets: [...].
How do I type an array of objects in TypeScript?
Define the item shape, then use []:
type Status = { id: number; name: string };
const statuses: Status[] = [];
Can I declare the type inline instead of creating a separate type?
Yes.
const statuses: { id: number; name: string }[] = [];
This is fine for small cases.
Should I use type or interface for object arrays?
Either is fine here. Use whichever style is preferred in your codebase.
Why does TypeScript catch .nammme but not always ?
Mini Project
Description
Build a small TypeScript status registry that demonstrates both ways of storing structured data: as an array of status objects and as a lookup object keyed by ID. This helps you practice choosing the right collection type and safely accessing object properties.
Goal
Create a typed collection of statuses and write functions that read them safely without misspelling property names.
Requirements
- Define a reusable type for a status object with
idandname. - Create one collection as an array of statuses.
- Create another collection as a lookup object using numeric keys.
- Write a function that prints all status names from the array.
- Write a function that returns a status name from the lookup object by ID, or a fallback message if not found.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular @ViewChild Returning Undefined: Lifecycle, Child Components, and Fixes
Learn why Angular @ViewChild can be undefined, when it becomes available, and how to access child components correctly using lifecycle hooks.