Search code examples
typescriptargumentsparameter-passingdocumentation-generation

Argument vs options in Typescript


Based on this question Multiple arguments vs. options object

I am wondering how this does applies to Typescript. I would like to make a decision so that:

  • The hints given by the pre-compiler / lint will be as relevant as possible.
  • The generated documentation would also be as clean as possible.

Pro and cons I found now:

  • Typescript can handle optional arguments quite well, but relying on the parameter's order and having a long list of arguments is not something I find convenient.

  • The use of an option object is nice but would need to create an interface for each object (?), which in my understanding would overload the code and I do not know which kind of documentation / hints will be generated then.


Solution

  • In my opinion, the same still applies to TypeScript. Don't rely on the IDE to give you hints about what the code does. It's best to have the code tell you what it does.

    The more arguments you have, the less readable your code becomes

    Take the following code:

    sendMsg("Poem", "I sing of brooks, of blossoms, birds and bowers.", new Date(2015, 9, 20));
    

    We might be able to tell that the first argument is the title and second argument is the body, but what does the third argument do? What does the date mean here?

    We have to look at the function signature to see:

    function sendMsg(title: string, body: string, dateToSend = new Date())
    

    So now we know what the third parameter is, but even though we are using TypeScript, we still had to do some investigation and look at the function signature. Alternatively we could have moved our mouse over the function call for our development environment to tell us, but that's still not ideal.

    Too many arguments makes changes difficult and increases the chance of error

    Now say we wanted to add a new required date parameter called dateToSendAgain. Our function signature changes to this:

    function sendMsg(title: string, body: string, dateToSendAgain: Date, dateToSend = new Date())
    

    A problem with this, is that our original function call is not throwing a compile error and the meaning has changed:

    // now creates a message with dateToSendAgain = new Date(2015, 9, 20)
    // and dateToSend = new Date()
    sendMsg("Poem", "I sing of brooks, of blossoms, birds and bowers.", new Date(2015, 9, 20));
    

    Even though we originally intended dateToSend to be new Date(2015, 9, 20), it's now new Date() and maybe we didn't want dateToSend to be new Date(2015, 9, 20).

    Use an object with properties instead

    We could have solved all this by having our original function signature use an object with properties (note that an interface is not required):

    function sendMsg(options: { title: string; body: string; dateToSend?: Date; dateToSendAgain: Date; }) {
        // we now have to define our default values here though...
        // if we use destructuring it's not too bad:
        const {title, dateToSend = new Date()} = options;
        // ...rest of function body omitted...
    }
    

    So our original code would have looked like this:

    sendMsg({
        title: "Poem", 
        body: "I sing of brooks, of blossoms, birds and bowers.", 
        dateToSend: new Date(2015, 9, 20)
    });
    

    ...which is very easy to quickly understand what is going on.

    Additionally, when we go to add dateToSendAgain, it would be very easy and we would get a compile error informing us to update all our function calls with the new required property:

    sendMsg({
        title: "Poem", 
        body: "I sing of brooks, of blossoms, birds and bowers.", 
        dateToSend: new Date(2015, 9, 20),
        dateToSendAgain: new Date(2015, 10, 20)
    });
    

    Suggestion

    My suggestion would be to:

    1. Use multiple parameters when there's not too many and you can understand the meaning of each argument by looking at the function name.
    2. Otherwise use an object with properties.
    3. Mixing these two together is ok if it's readable.

    Object Property Documentation

    TypeScript uses JSDoc for code completion and so you can use this syntax for documenting properties of the object. Read here on how to document it.

    Unfortunately this doesn't seem to give me the description for the object properties in code completion in Visual Studio 2013 with TS 1.6.

    Using an interface seems to work though:

    /**
     * The options for sendMsg
     */
    interface SendMsgOptions {
        /**
        * The title of the message
        */
        title: string;
        // etc...
    }
    

    Changing function header:

    function sendMsg(options: SendMsgOptions)
    

    Then when using it you can see the comment in code completion:

    Message options code completion