Repository navigation
console, doc, util: console.log() and util.format() are wrongly documented and may be inconsistent #13908
Description
Activity
- addedconsoleIssues and PRs related to the console subsystem.Issues and PRs related to the console subsystem.docIssues and PRs related to Node.js documentation.Issues and PRs related to Node.js documentation.utilIssues and PRs related to the built-in util module.Issues and PRs related to the built-in util module.
on Jun 25, 2017 - changed the title
[-]console.log and util.format with non-format string[/-][+]console, doc, util: console.log() and util.format() are wrongly documented[/+]on Jun 25, 2017 - changed the title
[-]console, doc, util: console.log() and util.format() are wrongly documented[/-][+]console, doc, util: console.log() and util.format() are wrongly documented and may be inconsistent[/+]on Jun 25, 2017 IMO if the first argument is not a format string, i.e. it could be a string type but with no %-characters (unless you \-escape them), all arguments should be called with
util.inspect()for consistency purpose.Reacted by Vse Mozhe ButyI am really not sure if this should also be applied for all excessive arguments after a format string. Both ways can be considered somehow inconsistent.
@vsemozhetbyt What would be the option? To throw an error?
I think throwing would be needlessly obstructive. Maybe this case should be just documented more clearly. Because if we apply inspect-all-format-not-respective-arguments here, this could be more confusing:
> console.log('foo %s', 'str', 'str'); foo str 'str' // instead of current: foo str str
I think it seems consistent. If you for any case would want to write that string you simply write
console.log('foo %s str', 'str')?Or
console.log('foo %s %s', 'str', 'str');. Well, maybe this makes sense.However, this should be widely discussed, as these ones are rather heavily used functions across the userland.
Reacted by Natanael LogI do not know how this can be worded in good English with not so confusing examples, but this is some dry summation.
Various sets of arguments with different behavior and results.
-
String with N placeholders + N arguments = string where all placeholders are replaced with converted arguments.
-
String with N placeholders + zero or less than N arguments = string where placeholders without corresponding arguments are not replaced.
-
String with N placeholders (including zero) + more than N arguments = string where placeholders are replaced with converted corresponding arguments + space + extra arguments coerced into strings and concatenated with spaces (
util.inspect()is used for arguments whosetypeofis 'object' (exceptnull) or 'symbol'). -
Non-string argument + zero or more arguments = string where all arguments coerced into strings and concatenated with spaces (each argument is converted to a string using
util.inspect()) -
No arguments = empty string (or
'\n'fromconsole.log()). The doc forutil.format()may also be corrected here, as the first argument is not mandatory (this is a useless case, nevertheless, this may be clarified).
const f = () => {}; const s = 'str'; const o = new Array(3); console.log('%j %s %j'); console.log('%j %s %j', f); console.log('%j %s %j', f, s, o); console.log('%j %s', f, s, o); console.log('', f, s, o); console.log(f, s, o); console.log();
%j %s %j undefined %s %j undefined str [null,null,null] undefined str [ <3 empty items> ] () => {} str [ <3 empty items> ] [Function: f] 'str' [ <3 empty items> ] < '\n' >
const f = () => {}; const s = 'str'; const o = new Array(3); > util.format('%j %s %j'); '%j %s %j' > util.format('%j %s %j', f); 'undefined %s %j' > util.format('%j %s %j', f, s, o); 'undefined str [null,null,null]' > util.format('%j %s', f, s, o); 'undefined str [ <3 empty items> ]' > util.format('', f, s, o); ' () => {} str [ <3 empty items> ]' > util.format(f, s, o); '[Function: f] \'str\' [ <3 empty items> ]' > util.format(); ''
cc @nodejs/documentation, @bnoordhuis, @cjihrig, @evanlucas
-
Would it be of interest if I submitted a PR with this proposal realised?
@nattelog I hope a doc PR would draw some more attention at least)
@vsemozhetbyt And by doc PR you mean one where I document the util.format() function?
@vsemozhetbyt I added a small check to this if statement:
if (typeof f !== 'string' || !/%\w/.test(f))
To guard against non-format strings. However, it makes many tests fail. See this for instance:
=== release test-cli-eval === Path: parallel/test-cli-eval assert.js:60 throw new errors.AssertionError({ ^ AssertionError [ERR_ASSERTION]: '\'start\'\n\'beforeExit\'\n\'exit\'\n' === 'start\nbeforeExit\nexit\n' at Object.<anonymous> (/Users/nattelog/Projekt/node/test/parallel/test-cli-eval.js:174:10) at Module._compile (module.js:569:30) at Object.Module._extensions..js (module.js:580:10) at Module.load (module.js:503:32) at tryModuleLoad (module.js:466:12) at Function.Module._load (module.js:458:3) at Function.Module.runMain (module.js:605:10) at startup (bootstrap_node.js:158:16) at bootstrap_node.js:575:3 Command: out/Release/node /Users/nattelog/Projekt/node/test/parallel/test-cli-eval.jsSince
util.inspect()will be called on every argument passed now, there will be more cases ofutil.format()outputs with an extra'-character around strings which will cause assertion errors like the one in the example.If this change will be implemented, many test cases must be changed as well.
@nattelog I think the doc for
util.format()(and maybe some cleanup and hint in the doc forconsole.log()) would be a good start and a base for backports to docs for all actual versions.If you like to unify the API behavior, it would be better to raise a separate PR for this.
Yes, this change will be a drastic one and many tests will need fixing, so this will be some time-consuming project. And the guard also should be more careful (currently it would erroneously consider strings like
' %%s 'or' %_wrong 'as format strings ).So maybe we should get some +1 from CTC members before somebody dares to undertake this fix.
- added 2 commits that reference this issue
on Jul 1, 2017 - added a commit that references this issue
on Jul 11, 2017 - added a commit that references this issue
on Jul 18, 2017 - added a commit that references this issue
on Jul 19, 2017 - added a commit that references this issue
on Jul 27, 2026
This is an issue that superseded #6341, Previously I thought this was a pure doc issue, now it seems to me that it is a more complicated problem that needs more discussion.
The both docs fragments (here and here) are wrong:
util.inspect()is not called on each argument if the first argument is a non-format string. This is true if the first argument is not a string at all (see this path in code). If the first argument is a non-format string,util.inspect()is called only for arguments whosetypeofis'object'or'symbol'(exceptnull) — see this path in code.Currently, I've found out that this impacts the output with
StringandFunctionarguments (watch out for quotes in the output for strings and absolutely different output for functions):Maybe there are other diferences.
Possible solutions:
UPD: This fragment is more correct:
As you can see,
util.inspect()is not called for excessiveStringandFunctionarguments here.However, this doc fragment can also be improved, as functions are objects (maybe
typeofshould be mentioned).