Let see an approach to define developer-friendly function signatures with the named-arguments pattern and TypeScript.
Passing multiples arguments to a function can leads to several issues:
itemIdis fine but what are behind the
Those issues apply for both the developer calling the function and the one reviewing the resulting code.
The more arguments you pass to a function, the faster it will become a nightmare to call it.
TypeScript can help you by providing an IDE integration displaying the function input naming.
Type checking can also help you in some cases with the order.
But you still have to pass the full argument list even though you only want to use 2 of them. Plus the TypeScript « auto documentation » may not be available when performing a read-only review of some code, on GitHub for example
So let’s jump in a solution that resolve all those issues.
Named-argumentspattern (with TypeScript)
This pattern has always been available but it has been made easy with es6 Object destructuring and TypeScript
Using a single object as a wrapper and using properties to pass arguments will resolve all the issues:
true value makes perfect sense now!
Plus, we don’t need anymore to pass extra values for optional parameters. No more extra
null values !
If we want to add some optional parameters, we just need to define the related properties (
So now, let’s have a look of such a function declaration
Using a single object as an argument will make it more obtrusive to guess the input and harder to retrieve the values?
Not at all, thanks to ES6 object destructuring that can be used straight to function inputs
It looks like the classic Array-like argument list (positional arguments) with some extra brackets around
Notice that we are using default parameters to define optional arguments:
createdBefore property is missing (
null value will be set automatically.
disabled argument if omitted will be set to
If you are familiar with React you have notice that this mechanism is used for Component props, taking advantage that adding attributes to a HTML( JSX) element has the same behavior that adding properties to an object:
Although all the issues as been resolved using our object, the developer experience will be really smooth when coupled with TypeScript. Your function API will be auto-documented showing to the developer the available property names and their related types.
Sometimes, the name of a property is not enough to understand the type of the value that should be used. Should a date argument be a Date object ? A formatted string ? Or a timestamp ?
TypeScript will give you this information.
The input interface gives a good understanding of the arguments used by the function.
At a glance we can see which arguments are optionals thanks to the
If you omit a mandatory argument, TypeScript will notify you.
So TypeScript will help both the developer calling the function and a reviewer reading the code from the function itself
Cherry on the cake, shortand property names can ease the process of calling such a function.
If you’re passing values using variable names matching the expected properties, you’ll just have to call the function wrapping the variables inside brackets
It is the same thing than
If one of your variable name doesn’t match an expected property name, you can of course mix shorthand property names with classic declarations.
The nice thing with the
named-arguments pattern is that the more arguments are required for a function, the more sense it will make to use it.
That’s not because this pattern has some advantages that you should use it everywhere:
If your function accepts a single argument, it doesn’t make sense to wrap it in an object.
You should just ensure that the naming give a good hint of the input nature. Using TypeScript won’t even make it an issue.
You could be tempted to use named-argument because of potential future evolution of your API but you shouldn’t forecast it by sacrifying simplicity. If you need additional arguments later, let’s refactor it at this moment.
In my opinion, if your function only accepts 2 arguments, it is still excessive to use this pattern, especially if the second one is optional.
But it could be a good call to use named-arguments if the 2 properties have the same types.
For example building a range of values (min/max) or a range of dates (start/end)
The order may be important and TypeScript won’t help in this case.
Keep in mind that if you have a lot of properties in your argument object, this can be a good hint that you are doing too many things at once.
For example, if you return an object using the
RORO (Receive Object / Return Object) pattern, you could split your function into multiple sub tasks using composition.
As always, when your single input and output share the same type, it can be a good hint that you can enable composing, splitting the manipulation on your input data on multiple functions.