Prettier 1.19.1
Playground link
Input:
export function *flattenChildren(children: Children): Iterable<Child> {
if (typeof children === "string" || !isIterable(children)) {
yield children;
return;
}
for (const child of children) {
yield *flattenChildren(child);
}
}
Output:
export function* flattenChildren(children: Children): Iterable<Child> {
if (typeof children === "string" || !isIterable(children)) {
yield children;
return;
}
for (const child of children) {
yield* flattenChildren(child);
}
}
Expected behavior:
Input unchanged.
This is a small nit I have with prettier, which is that it puts a space after generator stars and no space before. I believe this is suboptimal because in all cases, these stars act like a prefix operator, which prettier in all other cases puts flush against its operand (!value, ++i). Treating generator stars like prefix operators is both more consistent and helps catch bugs.
Consistency
There are currently two places in javascript where stars can be used to create generator functions:
- after the function keyword in function declarations and expressions:
function *fizzbuzz() {
/* ... */
}
- before method names in classes and object literals.
class FizzBuzzer {
*[Symbol.iterator]() {
/* ... */
}
}
const fizzbuzz = {
*[Symbol.iterator]() {
/* ... */
},
};
In the first case, it’s not exactly clear where the star goes and prettier places the star against the function keyword. In the second case, there’s nothing before the star, and even prettier puts the star against the method name. Why do we treat these two cases differently? Insofar as there is no “before” to put the star against in the case of class/object method declarations, we should prefer to put the star against function names as well for the purposes of consistency.
The one instance of inconsistency which this rule causes is the case of anonymous generator expressions.
Here it would seem that putting the star against the parameter list is inconsistent because while there is a space before the star in named functions, there isn’t a space before the star in anonymous functions. I concede this point, and find that it is further evidence that we should simply add a space after all function keywords consistently (see issue #3847).
In the case of yield stars, adding a star without an expression is simply a syntax error:
yield*;
// ^ parser expects an expression
This is more evidence that generator stars should be treated like prefix operators.
Catching bugs
Function stars change the return value of functions and yield stars delegate yielding to the yielded expression. By putting these stars against the keywords function or yield, you increase the chance that a developer will miss that the function is a generator, or that the yield is being delegated. Programmers will often gloss over keywords like function or yield when reading code because they are common and unchanging, while the names of functions and the contents of yielded expressions are critically important to read and make sense of, if only to catch typos. Most syntax highlighters will also highlight stars the same color as the keyword function and yield, compounding the problem.
Consider this actual bug I have personally made, which cannot be type checked away:
function* getRawText(): Iterable<string> {
/* some logic to get an iterable of tokens */
for (const token of tokens) {
yield* token.getRawText();
}
}
Because strings are themselves iterables of strings (where each iteration yields a character), a type checker would not notice that the developer was accidentally yielding each token‘s text character by character. However, this is almost certainly be a bug. By placing the star flush against the expression:
yield *token.getRawText();
we make it more obvious that we are delegating to the expression, no matter how busy the expression becomes.
A similar argument can be made for function/method names. Generator functions are lazy, so it is critical that developers understand that a function returns a generator object and use the generator object in some way to execute the generator. By placing the star against the name of the function, we make it clear to readers that the function returns a generator object and executes lazily.
Possible Objections
I would argue in response that they aren’t keywords, and there isn’t a single example of a “keyword” which permits spaces between its members, or even has “members” to begin with. The keywords are function and yield, and while the stars modify the behavior of the keywords (they change the return value of a function or delegate yielding), they do not change the fact that we are declaring a function/yielding from a generator.
- Putting a space after stars is just established “convention.”
I would argue that there is no clear consensus about how to space generator stars, and that any “conventions” were established before generators came to be used regularly. I use generators regularly in code I write, and I have provided two objective points as to why the convention should be as I described. In addition, there is the convention of prefix operators, and I believe I’ve established that stars are more similar to prefix operators than postfix operators (even though they are neither).
Therefore, I propose prettier uniformly place generator and yield stars flush against whatever follows, rather than whatever came before. This should be the default behavior, and not an option. This can be done if/when #3847 is done.
Prettier 1.19.1
Playground link
Input:
Output:
Expected behavior:
Input unchanged.
This is a small nit I have with prettier, which is that it puts a space after generator stars and no space before. I believe this is suboptimal because in all cases, these stars act like a prefix operator, which prettier in all other cases puts flush against its operand (
!value,++i). Treating generator stars like prefix operators is both more consistent and helps catch bugs.Consistency
There are currently two places in javascript where stars can be used to create generator functions:
In the first case, it’s not exactly clear where the star goes and prettier places the star against the
functionkeyword. In the second case, there’s nothing before the star, and even prettier puts the star against the method name. Why do we treat these two cases differently? Insofar as there is no “before” to put the star against in the case of class/object method declarations, we should prefer to put the star against function names as well for the purposes of consistency.The one instance of inconsistency which this rule causes is the case of anonymous generator expressions.
Here it would seem that putting the star against the parameter list is inconsistent because while there is a space before the star in named functions, there isn’t a space before the star in anonymous functions. I concede this point, and find that it is further evidence that we should simply add a space after all function keywords consistently (see issue #3847).
In the case of yield stars, adding a star without an expression is simply a syntax error:
This is more evidence that generator stars should be treated like prefix operators.
Catching bugs
Function stars change the return value of functions and yield stars delegate yielding to the yielded expression. By putting these stars against the keywords
functionoryield, you increase the chance that a developer will miss that the function is a generator, or that the yield is being delegated. Programmers will often gloss over keywords likefunctionoryieldwhen reading code because they are common and unchanging, while the names of functions and the contents of yielded expressions are critically important to read and make sense of, if only to catch typos. Most syntax highlighters will also highlight stars the same color as the keywordfunctionandyield, compounding the problem.Consider this actual bug I have personally made, which cannot be type checked away:
Because strings are themselves iterables of strings (where each iteration yields a character), a type checker would not notice that the developer was accidentally yielding each token‘s text character by character. However, this is almost certainly be a bug. By placing the star flush against the expression:
we make it more obvious that we are delegating to the expression, no matter how busy the expression becomes.
A similar argument can be made for function/method names. Generator functions are lazy, so it is critical that developers understand that a function returns a generator object and use the generator object in some way to execute the generator. By placing the star against the name of the function, we make it clear to readers that the function returns a generator object and executes lazily.
Possible Objections
function*/yield*a keyword? Doesn’t it look like a keyword?I would argue in response that they aren’t keywords, and there isn’t a single example of a “keyword” which permits spaces between its members, or even has “members” to begin with. The keywords are
functionandyield, and while the stars modify the behavior of the keywords (they change the return value of a function or delegate yielding), they do not change the fact that we are declaring a function/yielding from a generator.I would argue that there is no clear consensus about how to space generator stars, and that any “conventions” were established before generators came to be used regularly. I use generators regularly in code I write, and I have provided two objective points as to why the convention should be as I described. In addition, there is the convention of prefix operators, and I believe I’ve established that stars are more similar to prefix operators than postfix operators (even though they are neither).
Therefore, I propose prettier uniformly place generator and yield stars flush against whatever follows, rather than whatever came before. This should be the default behavior, and not an option. This can be done if/when #3847 is done.