blog   |   26 Dec, 2022

TS: Use desrtucturing objects instead of function parameters

In JavaScript, functions act as contracts, specifying what inputs are needed to get certain outputs. However, these functions tend to evolve, posing challenges in maintaining compatibility.

Imagine a basic function that calculates the area of a square based on its side length. Initially, it meets the requirements, but as needs expand, there arises a demand to calculate areas for rectangles and circles while still working with the existing setup.

The common approach involves tweaking the existing function to handle multiple scenarios within the same function. For example, modifying it to accommodate rectangles by repurposing the side length parameter for both width and height, followed by additional tweaks for circles.

Yet, this gradual evolution introduces challenges in readability. Without knowing how the function works, understanding which parameter stands for what becomes confusing. Though IDEs offer help through autocompletion and descriptions, they don't address the growing complexity within the function itself.

Moreover, extending the function's capabilities creates issues when implementing the new functionality, particularly in plain JavaScript. Errors in passing arguments become more likely with each new parameter added.

The solution lies in simplifying the function by using an object as a single parameter.

Initial function for square areas:

function calculateArea(sideLength: number ): number {
    return sideLength * sideLength;
}

Making it more efficient with destructuring object for calculating area of both rectangle and square, ensuring backward compatibility:


type ShapeOptions = { shape: string; width?: number; height?: number; };
function calculateArea({ shape, width, height}: ShapeOptions): number { if (shape === 'square') { return width ? width * width : 0; // Assuming width as side length for squares } else if (shape === 'rectangle') { return width && height ? width * height : 0; } return 0; }

Expanding further to include circles:

type ShapeOptions = {
    shape: string;
    width?: number;
    height?: number;
    radius?: number;
};

function calculateArea({ shape, width, height, radius }: ShapeOptions): number {
    if (shape === 'square') {
        return width ? width * width : 0;
    } else if (shape === 'rectangle') {
        return width && height ? width * height : 0;
    } else if (shape === 'circle') {
        return radius ? Math.PI * radius * radius : 0;
    }
    return 0;
}

And calling this function with different shapes is like a breeze

calculateArea({ shape: 'square', width: 20 });
calculateArea({ width: 20, height: 40, shape: 'rectangle' });
calculateArea({ shape: 'circle', radius: 50});

This approach simplifies parameter handling, ensuring clarity, flexibility, and backward compatibility as the function evolves.