N-Params vs. Single Param
carlos-menezes.com
carlos-menezes.com
One nice unexpected side effect is that I end up with more consistency in variable naming when using a param object in order to benefit from object definition shortcuts.
ie I will usually write something like
const firstName = getFirstName(); doTheThing({ firstName });
rather than const fName = getFirstName(); doTheThing({ firstName: fName });But in the example from the OP, there isn’t really a primary argument. You're dealing with multiple values that carry equal weight, which makes positional arguments harder to reason about at a glance.
string combineName(string first, string last);
Okay, I assume the result is the full name, but I don't really know that like I would if the return had a name in the header. (The function can be getFullName, yes, but that's so simple it doesn't matter).
type FullName = string;
function combineName(first: string, last: string): FullName;
Also documentation comment for function is what specifically solves this - describes what function does and returns. function combineNames({ first, last }) {
const fullName = `${first} ${last}`;
return { fullName };
}
const { fullName } = combineNames({first: 'John', last: 'Doe' }); const [state, setState] = useState(initialState)
instead of const {state, setState} = useState(initialState)A more complex example will probably return a more complex type that is more self-documenting. If the type is a single scalar value then I don't see the value of adding another name here.
Then pair it with destructuring assignment!
`const { fullName } = getName({ firstName, lastName )}`
In fact, just forget that it is a function that does something. Treat it as the thing it returns.
string fullName( string firstName, string lastName )
But in native languages like C/C++/etc, this pattern usually hurts performance. Separate arguments go into registers (faster but depends on number of arguments), while structs often involve indirection, and sometimes alignment/padding issues. Also, passing a const struct doesn’t guarantee the fields inside are truly const.
That said, it's context dependent. If you're passing stuff through multiple layers or want a more stable function signature, using a struct can be cleaner. Just not something I’d generalize to any language or any use case.
I don't think we are near said dream, yet.
Functions with plain arguments and a struct both produce identical assembly output.
Here what happens with optimizations: https://godbolt.org/z/G18zd7chP
Look at the registers usages vs stack
https://rust-lang.github.io/rust-clippy/master/index.html#to...
I agree with others that the destructuring is a nice syntax to read and write. Yet, I have started to feel it’s an overused pattern, deployed by habit rather than consideration (in my code anyway). I can’t put my finger on why. It’s possibly a gut-feel that my functions would be better taking feeer arguments.
[1] https://samkrit.ch/2025/03/09/programming-1.html#anonymous-t...
Now I'm gonna go read your blog series.
def my_func(a: int, b: int):
print(a+b)
# both of these can be used
my_func(1, 2)
my_func(a=1, b=2)``` Point = namedtuple('Point', 'x y') pt1 = Point(1.0, 5.0) ```
And then call the X or Y coordinates either by index: pt1[0], pt1[1], or coordinate name: pt1.x, pt1.y.
This can be a really handy way to help people understand your code as what you are calling becomes a lot more explicit.
[0] https://stackoverflow.com/questions/2970608/what-are-named-t...
def my_func(a: int, b: int):
...
def my_func2(args: my_func.__args_type__):
...
with `my_func.__args_type__` standing for an object like `{a: int, b: int}`.1. Most of the time, arguments are not the same type, so if you're using TypeScript, you're already getting errors at this point. In your example, only first/last names might get mixed up. And if you still get those wrong while writing that code, you're just phoning it in atp and maybe should take a day off.
2. The same TypeScript that checks your types, also lets you see the signature when hovering over the function.
In real world coding, this isn't an issue, and you'll probably give up keeping this "rule" after a year or two. But good that you're at least thinking about these kinds of things.
As a counter-example, I prefer
setEnabled(item, true);
to setEnabled({item: item, isEnabled: true});https://testing.googleblog.com/2024/05/avoid-long-parameter-...
type TBinarySearchParams<T> = {
needle: T;
haystack: [T];
};
const binarySearch = <T,>(args: TBinarySearchParams<T>) => { ... }
binarySearch({needle: 42, haystack: [1,2,3]});
Or at least, I would argue they are not better, but if you still insist: type TIsEvenParams = {
num: number;
};
const isEven = (args: TIsEvenParams) => { ... }
isEven({num: 7});
Surely that is strictly worse?A draw back would be is you want to overload such a method with fewer parameters, it then becomes weird.
With the N-params pattern it is not possible to create an interface on this method because the types of each param may be different across different implementation.