TypeScript How to refactor a function to use named parameters

eye-catch JavaScript/TypeScript

It’s not readable if a function requires many arguments. In this case, named parameters make it readable but TypeScript or JavaScript doesn’t have such a feature. How can we improve this situation?

Sponsored links

Using object for named parameters

Assume that we already have the following function.

function doSomething(
    service: string,
    user: string,
    isFeature1Enabled: boolean,
    isFeature2Enabled: boolean,
    currentState: number,
) {
    console.log(service);
    // do something
}

We call it from another file.

doSomething("service1", "yuto", true, false, 0);

We can infer the first two arguments but not for other arguments. If we have enough time to refactor the caller we can change the function like this.

function doSomething(
    args: {
        service: string,
        user: string,
        isFeature1Enabled: boolean,
        isFeature2Enabled: boolean,
        currentState: number,
    }
) {
    console.log(args.service);
    // do something
}

A caller has to wrap the arguments and add the name.

doSomething({
    service: "service1",
    user: "yuto",
    isFeature1Enabled: true,
    isFeature2Enabled: false,
    currentState: 0,    
});

It looks better than before even though the code length increases. It is like named parameters.

Sponsored links

How to refactor the existing code

What should we do if there are lots of callers and we don’t want to refactor them at once? The easiest way is to define the parameter before calling it.

const service = "service1";
const user = "yuto";
const isFeature1Enabled = true;
const isFeature2Enabled = false;
const currentState = 0;
doSomething(service, user, isFeature1Enabled, isFeature2Enabled, currentState);

We can address it one by one in this way. However, it still needs refactoring at once when we want to change the declaration of the function.
Is there another way for the refactoring? Yes, but it still needs refactoring but we can proceed one by one.

Step 1

Create a function to generate the parameter list.

function createParameters(
    args: {
        service: string,
        user: string,
        isFeature1Enabled: boolean,
        isFeature2Enabled: boolean,
        currentState: number,
    }
}) {
    return [args.service, args.user, args.isFeature1Enabled, args.isFeature2Enabled, args.currentState];
}

Step 2

Use it with a spread operator.

const params = createParameters({
    service: "service1",
    user: "yuto",
    isFeature1Enabled: true,
    isFeature2Enabled: false,
    currentState: 0,    
});
doSomething(...params);

Step 3

Once we replace the function call with this, we can go to the next step.
Update the declaration of the function.

function doSomething(
    args: {
        service: string,
        user: string,
        isFeature1Enabled: boolean,
        isFeature2Enabled: boolean,
        currentState: number,
    }
) {
    console.log(args.service);
    // do something
}

Step 4

Change the return type to the object.

function createParameters(
    args: {
        service: string,
        user: string,
        isFeature1Enabled: boolean,
        isFeature2Enabled: boolean,
        currentState: number,
    }
}) {
    return args;
}

Step 5

Remove the createParameters function and put the object directly to doSomething function.

doSomething({
    service: "service1",
    user: "yuto",
    isFeature1Enabled: true,
    isFeature2Enabled: false,
    currentState: 0,    
});

That’s all. The total amount of work increases but we can refactor one by one in this way. It’s up to you how to refactor. You might want to stop on step 2.

A related article for a better refactoring

I showed you how to refactor the function parameter list but there is another and better way for it. Check the following article if you learn a better way.

Comments

Copied title and URL