w3ctag / design-principles

A small-but-growing set of design principles collected by the TAG while reviewing specifications
https://w3ctag.github.io/design-principles
178 stars 46 forks source link

New principle: Prefer dictionary arguments for all optional parameters (and non-obvious parameters?) #366

Closed LeaVerou closed 1 year ago

LeaVerou commented 2 years ago

Right now we have weaker guidance: 6.4. Prefer dictionary arguments over primitive arguments. However, I think dictionary arguments are preferable even for non-primitive arguments, if they are optional or non-obvious by looking at their value. E.g. consider fetch(), imagine if headers was positional simply because it's an object.

Dictionary arguments are preferable because:

WDYT?

LeaVerou commented 2 years ago

From today's breakout:

Some examples:

addEventListener("click", evt => doSomething(), { capture: true });

The first two positional arguments are "obvious", despite the first one even being a primitive.

promise.then(val => doSomething(val), err => handleError(err));

The first callback is more obvious than the second one, since it's central to the function's purpose.

new Date().toLocaleString("en-GB", {dateStyle: "long"});

Locale is obvious despite being a string. Also central to the function's purpose. All other args are correctly in a dictionary.

array.sort((a, b) => b.foo - a.foo);

I'd argue this is still obvious, as it's central to the sort function's purpose.

JSON.stringify(obj, (key, value) => {
    if (value instanceof Date) { return value.toString() }
    return value;
}, "\t");

Here obj is central to the function and this more obvious than the replacer argument, or the indent argument, which should have been in a dictionary.

Object.defineProperty(obj, "foo", {
    value: 4,
    enumerable: true
});

First two arguments (object and property name) obvious as they are central to the function''s purpose, everything else is (correctly) in a dictionary.

'9'.padStart(2, "0");

Here IMO arguments are non-obvious, despite being central to the function's purpose. Strings are used as freeform text rather than an enum, which tends to make them less obvious when used as a positional argument.

element1.replaceWith(element2);
container.replaceChild(element2, element1);

All positional arguments here are central to the function, but the latter is confusing (non-obvious) because both are of the same type and could have been in either order.

LeaVerou commented 2 years ago

Following today's breakout we still have consensus that all optional arguments should be dictionaries. We do not have consensus about mandatory arguments, so we will leave that out for now. @LeaVerou to draft the edits.