4

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.

Community
  • 1
  • 1
Flavien Volken
  • 19,196
  • 12
  • 100
  • 133
  • I'm not sure what you meant by "overload the code", but an interface for an options object is just good practice. If you didn't, you'd probably write a big block comment explaining it anyway. – thoughtrepo Oct 05 '15 at 18:25
  • By "overloading the code" I meant a big block of code describing the interface for a single option object. – Flavien Volken Oct 05 '15 at 19:20

1 Answers1

9

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

Community
  • 1
  • 1
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • Good, in most of the case I would only put the optional arguments into an option object. Now Is TS able to comment an object's property to give a hint ? F.e. */@body the content of your message/* ? – Flavien Volken Oct 05 '15 at 19:29
  • 1
    @FlavienVolken it doesn't work for me. I added the section "Object Property Documentation" to the answer with more details on this. – David Sherret Oct 05 '15 at 20:03
  • 1
    In that case I would've just made an interface, and commented the property of said interface. You can put JSDoc comments directly in the argument list, but it has to be on multiple lines, and looks ugly. – thoughtrepo Oct 05 '15 at 21:04
  • @thoughtrepo I agree. Btw can you give a simple example on how to comment a property of an interface ? I do think about ```@param {string} employee.name - The name of the employee.``` but it means I would have to manually specify the type of the parameter (within the comment) instead of using the type of the argument itself. I would obviously prefer the code to explicitly generate the right type into the doc. [EDIT] ok it looks to be only /** Your description */ before the property's definition. Thanks a lot! – Flavien Volken Oct 06 '15 at 05:53
  • 1
    @FlavienVolken yes it is only that. I have gone ahead and updated the post anyways. – David Sherret Oct 06 '15 at 17:22
  • I noticed that I'm allowed to set a default value for the options argument as you have it above like this... `makeCube(options: { width: number, depth: number, height: number } = {width:15,depth:15,height:15}) { }` Is that advisable? – Jeremy Foster Mar 25 '16 at 20:54
  • @JeremyFoster sure, or you could do something like this: `makeCube({ width = 15, depth = 15, height = 15 }) { /* use width, depth, and height here */ }` (See another example [here](http://stackoverflow.com/a/34769975/188246)) – David Sherret Mar 28 '16 at 04:32
  • @JeremyFoster they're still typed, just implicitly – David Sherret Mar 28 '16 at 23:08