TypeScript: How to Maintain Order of Object Properties

Updated: January 8, 2024 By: Guest Contributor Post a comment

Introduction

TypeScript, as a superset of JavaScript, offers a robust platform for the development of large-scale applications by providing static types. However, managing the order of object properties can pose unique challenges. This tutorial explores techniques to maintain property order in TypeScript objects, ensuring predictability and consistency within your codebase.

Understanding Object Property Order

In JavaScript, the order of object properties is not guaranteed, especially when keys are numeric. This can lead to unexpected behavior. ES2015 rules specify an order: integer keys in ascending order, followed by string and symbol keys in the order they were added. TypeScript adheres to these rules, although this is a runtime feature and not a type system one. Let’s see an example:

let myObject = { 35: 'age', name: 'John Doe', 12: 'years of experience' };
console.log(Object.keys(myObject)); // output: ['12', '35', 'name']

Using Map for Guaranteed Order

A Map object can maintain the order of its elements. In TypeScript, using Maps can provide predictability in the order that keys are iterated over. Below is how you can use Maps:

let employees = new Map<number, string>();
employees.set(100, 'Alice');
employees.set(2, 'Bob');
employees.forEach((value, key) => { console.log(`ID: ${key}, Name: ${value}`); }); // output respects insertion order

Ordering with Tuples and Arrays

Instead of typical objects for maintaining order, consider using tuples or arrays of tuples. They can ensure consistent order and can also be strongly typed in TypeScript. A tuple declaration might look like this:

type Employee = [number, string]; // Declare a tuple type
let employees: Employee[] = [[100, 'Alice'], [2, 'Bob']];
employees.forEach(elem => { console.log(`ID: ${elem[0]}, Name: ${elem[1]}`); });

Custom Wrapper Classes for Typed Objects

To facilitate order maintenance and enhance type-checking, you can create custom wrapper classes. This allows for the encapsulation of object property order logic and type safety provided by TypeScript. Here’s a basic example of a custom class:

class OrderedEmployeeRecord {
    private items: {[key: number]: string} = {};

    add(key: number, value: string): void {
        this.items[key] = value;
        // Logic to maintain order...
    }

    get(key: number): string {
        return this.items[key];
        // Additional get logic...
    }

    // More methods for order control...
}

Sort Functions for Object Keys

If maintaining the initial insertion order is not necessary, you could opt for sorting the keys of your objects before iterating over them. TypeScript’s type system can assist with creating sorter functions that are type-aware:

interface EmployeeRecord {
    [id: number]: string;
}

function sortedEmployeeKeys(empRecord: EmployeeRecord): number[] {
    return Object.keys(empRecord).map(key => parseInt(key)).sort((a, b) => a - b);
}

let empRecord: EmployeeRecord = { 100: 'Alice', 2: 'Bob' };
sortedEmployeeKeys(empRecord).forEach(key => { console.log(`ID: ${key}, Name: ${empRecord[key]}`); });

Proxies for Fine-grained Control

Proxies can intercept and redefine operations on objects, such as property access and assignment. With Proxies, we can control the order of properties with a higher level of manipulation:

let employeeProxy = new Proxy({}, {
    get(target, prop) {
        // Custom logic for property retrieval...
    },
    set(target, prop, value) {
        // Custom logic for setting property in a specific order...
        return true;
    }
    // More traps for order control...
});

Conclusion

This tutorial ventured through various strategies to maintain the order of object properties in TypeScript. Ranging from permissible decorators like Maps and custom wrapper classes to utilizing proxies for intricate order manipulations, TypeScript developers are equipped with several tools to ensure the consistent ordering of object properties. While TypeScript’s static typing doesn’t strictly enforce property order, these strategies help manage order at runtime, maintaining clarity and integrity of data throughout your applications.