# Backwards compatible optionally named arguments

**URL:** <https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939>\
**Category:** 💡 Ideas\
**Created:** [August 20, 2021, 6:16am UTC](https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939 "2021-08-20T06:16:16Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![dspeyer](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dspeyer/32/965_2.png) [@dspeyer](https://es.discourse.group/u/dspeyer)\
**Post date:** [August 20, 2021, 6:16am UTC](https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939/1 "2021-08-20T06:16:16Z")

</div>

Inspired by the previous thread on [Optional Named Parameters](https://es.discourse.group/t/optional-named-parameters/198/8)...

```javascript
function stringify(data, ?{replacer, indent} ) {...}
// These are the same:
stringify(myData, undefined, 2)
stringify(myData, {indent: 2})
stringify(myData, undefined, {indent:2}) // (though this one is silly))
stringify(myData, {indent: 2, replacer:undefined}) // as is this
// These aren't
stringify(myData,x=>x+1,{indent:2,replacer:undefined}) // The positional value dominates
stringify(myData,{replacer:undefined},{indent:2}) // The non-final argument is not destructured

```

When a function declaration uses the `?{}` notation and is called with the last argument being an object, that object is deconstructed over the arguments that have not been assigned positionally.

Using this with arguments that will ever be objects is discouraged, though you can still pass them in named mode, so a function that takes objects as those arguments _on rare occasions_ might still use this feature. In order to avoid bugs in this scenario, we might raise an exception on unused keys in the passed object.

This has all the usual advantages of optionally named arguments, allowing programmers to consider clarity and compactness in the context of the code they're actually writing. This is familiar to us from python.

But by being explicit, it means that a function's author gets to decide whether name, ordering or both of arguments is part of the signature, so they won't break users by accident. Also, we're using standard destructuring that is already well understood.

You may have recognized the `stringify` function from my example. I got it from the `JSON` library. If it were being designed today, it would probably take the latter two arguments as named, but it predates object destructuring and changing the api would break far too much code. But shifting it to optional named arguments in this way allows old code to keep working and new code to use clearer calling.

There's a lot of functions in the JS standard library that would benefit from a non-breaking conversion to named arguments.

---

<div class="post-metadata">

**Author:** ![lightmare](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/lightmare/32/843_2.png) [@lightmare](https://es.discourse.group/u/lightmare)\
**Post date:** [August 20, 2021, 10:21am UTC](https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939/2 "2021-08-20T10:21:45Z")

</div>

> [@dspeyer](#):
>
> Using this with arguments that will ever be objects is discouraged, though you can still pass them in named mode, so a function that takes objects as those arguments _on rare occasions_ might still use this feature. In order to avoid bugs in this scenario, we might raise an exception on unused keys in the passed object.

I understand why you need to make this restriction, but there are some issues with it.

```javascript
function required(data, { constructor }) { return constructor; }
required("", { dummy: 0 }); // returns [Function: Object]
required("", []); // returns [Function: Array]

```

```javascript
function optional(data, ?{ constructor }) { return constructor; }
optional("", { dummy: 0 }); // throws because "dummy" unused?
optional("", []); // non-enumerable "length" unused, does it throw?
optional("", {}); // ???

```

Does the last line return the `constructor` from prototype? If so, why does it not throw because of unused properties on prototype? And if unused inherited properties are silently ignored, what if you later add a named parameter matching a previously ignored inherited property?

I think this restriction would lead to some surprising behaviour, unless you completely ignore the prototype, i.e. only consider own properties both for binding and for checking unused.

---

<div class="post-metadata">

**Author:** ![dspeyer](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dspeyer/32/965_2.png) [@dspeyer](https://es.discourse.group/u/dspeyer)\
**Post date:** [September 3, 2021, 4:42am UTC](https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939/3 "2021-09-03T04:42:48Z")

</div>

Hmm. This actually gets trickier than I'd realized. I hadn't been thinking about prototypes at all...

I used the term "not an object". That isn't actually a javascript concept. `let {toString} = 42;` is valid javascript. So when should we regard a terminal argument as nonobject for nondestructuring purposes?

Really, if you write

```javascript
function foo(?{data, length}) {...}
foo([1,2,3])

```

I would expect `data=[1,2,3]` and `length=undefined`, not an attempt to destructure an array as an object.

I think the rule should be that only objects with empty prototypes get destructured for optionally named arguments. This is a little surprising, since js generally doesn't make that distinction, but it lets us use generic destructuring, and I think it Does What We Mean in approximately all cases.

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [September 3, 2021, 5:07am UTC](https://es.discourse.group/t/backwards-compatible-optionally-named-arguments/939/4 "2021-09-03T05:07:37Z")

</div>

What is an "empty prototype"? Most objects don't have a null prototype, if that's what you mean. What about something that extends an object that in turn extends null?

There's a lot of destructuring use cases that need prototype chain lookup - there's a reason destructuring uses Get() instead of grabbing an own property.
