fixhunt

biome nursery/noUnnecessaryTemplateExpression

labelled safe
Runs / 2026-10-17T20-47-31-435Z-nk3c-biome-nursery_noUnnecessaryTemplateExpressionbiome 2.5.152026-10-17 20:47 UTC8 programs × 2 round(s)8 programs, 4 fired, 4 fixed, 0 findings, 3 rejectedmodel cost $0.0020

Analysis Nemotron 3 Super

Flags template literals containing only string literal interpolations that can be rewritten as concatenated or plain string literals.

Fix: Replaces the template literal with either a plain string literal if it has a single interpolation, or a string concatenation using + operator if it has multiple adjacent string literal interpolations, preserving original whitespace and quotes where applicable.

12 hazards
  • Rewriting `${'hello'}` to 'hello' changes behavior if the code relies on the template literal being a distinct object identity (e.g., for === comparison with another template literal).
  • Converting `${'a'}${'b'}` to 'a'+'b' may alter evaluation order if the original template was part of a larger expression where left-to-right timing mattered for side effects in surrounding expressions.
  • If the template literal appears in a context expecting a TemplateLiteral type (e.g., tagged template inference), replacing it with a string literal breaks type checking in TypeScript.
  • The autofix may incorrectly remove escaped characters like \n or \t within the string literals if they are present, altering the resulting string value.
  • When the template literal is immediately followed by a plus (+) without space, rewriting `'a'+'b'` could be misparsed as part of a larger concatenation chain due to operator precedence.
  • Comments inside the template literal (e.g., `${'hello' /* comment */}`) would be lost during the rewrite since string literals do not preserve internal comments.
  • If the string literal contains a line terminator (e.g., `${'a\nb'}`), rewriting it as a plain string literal may break if the surrounding code depends on the template literal's multi-line nature for ASI or parsing.
  • In strict mode, if the original template literal was used in an eval-like context, replacing it with a string literal could change how the string is interpreted due to differing escape handling.
  • The fix assumes all interpolated parts are pure string literals; if any interpolation is actually a non-literal (e.g., due to macro expansion), the rewrite would incorrectly remove necessary template syntax.
  • When the template literal is part of a sequence expression or comma operator, rewriting it may change how the expression is parsed or optimized by minifiers.
  • If the template literal is used as a property key in an object literal, rewriting it to a string literal may cause a syntax error if the original relied on template literal flexibility (though rare, as keys are coerced).
  • The autofix may insert unnecessary parentheses around the rewritten expression if not carefully formatted, potentially altering precedence in edge cases involving exponentiation or nullish coalescing.

Programs

idresulttitlehazard
r1-p001fix okEvaluation order changed when template literal part of larger expressionConverting `${'a'}${'b'}` to 'a'+'b' may alter evaluation order if the original template was part of a larger expression where left-to-right timing mattered for side effects in surrounding expressions
r1-p002silent prediction offType checking broken in tagged template contextIf the template literal appears in a context expecting a TemplateLiteral type (e.g., tagged template inference), replacing it with a string literal breaks type checking in TypeScript.
r1-p003fix okSequence expression parsing changed by rewriteWhen the template literal is part of a sequence expression or comma operator, rewriting it may change how the expression is parsed or optimized by minifiers.
r1-p004invalidInternal comments lost during rewriteComments inside the template literal (e.g., `${'hello' /* comment */}`) would be lost during the rewrite since string literals do not preserve internal comments.
r1-p005fix okTemplate literal as object property keyIf the template literal is used as a property key in an object literal, rewriting it to a string literal may cause a syntax error if the original relied on template literal flexibility (though rare, a
r1-p006fix ok__proto__ key handling affected by rewrite__proto__ keys, prototype access, Object.assign vs spread
r1-p007invalidthis binding affected in method extractionthis binding: arrow vs function, methods extracted from objects, call/apply/bind
r1-p008invalidExceptions in removed code change behaviorexceptions thrown inside the code the fix removes or rewrites

Sources

r1-p001 · Evaluation order changed when template literal part of larger expression
1let x = 0;
2const log = v => { console.log('log:', v); return ++x; };
3expr = (log(1), `${'a'}${'b'}`, log(2));
4console.log('Result:', expr);
--- orig.js	2026-10-17 16:47:42.635184249 -0400
+++ fixed.js	2026-10-17 16:47:42.918190776 -0400
@@ -1,4 +1,4 @@
 let x = 0;
 const log = v => { console.log('log:', v); return ++x; };
-expr = (log(1), `${'a'}${'b'}`, log(2));
+expr = (log(1), "ab", log(2));
 console.log('Result:', expr);
log: 1
log: 2
r1-p002 · Type checking broken in tagged template context
1function tag(strings, ...values) {
2 return { raw: strings, cooked: values };
3}
4const result = tag`${'hello'}${'world'}`;
5console.log('Tagged result:', JSON.stringify(result));
Tagged result: {"raw":["","",""],"cooked":["hello","world"]}
r1-p003 · Sequence expression parsing changed by rewrite
1let evalOrder = [];
2const push = v => { evalOrder.push(v); return v; };
3const expr = (push(1), `${'a'}${'b'}`, push(2), push(3));
4console.log('Eval order:', evalOrder.join(','));
5console.log('Expr value:', expr);
--- orig.js	2026-10-17 16:47:43.314199901 -0400
+++ fixed.js	2026-10-17 16:47:43.587206189 -0400
@@ -1,5 +1,5 @@
 let evalOrder = [];
 const push = v => { evalOrder.push(v); return v; };
-const expr = (push(1), `${'a'}${'b'}`, push(2), push(3));
+const expr = (push(1), "ab", push(2), push(3));
 console.log('Eval order:', evalOrder.join(','));
 console.log('Expr value:', expr);
Eval order: 1,2,3
Expr value: 3
r1-p004 · Internal comments lost during rewrite (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-47-3)
1// This tests near-miss: comment inside interpolation - rule should NOT trigger
2const msg = `${'hello' /* ignored */}${'world'`;
3console.log('Message:', msg);
r1-p005 · Template literal as object property key
1const obj = {
2 [`${'key'}${'name'}`]: 'value'
3};
4console.log('Key exists:', Object.hasOwn(obj, 'keyname'));
5console.log('Value:', obj.keyname);
--- orig.js	2026-10-17 16:47:43.812211373 -0400
+++ fixed.js	2026-10-17 16:47:44.086217682 -0400
@@ -1,5 +1,5 @@
 const obj = {
-  [`${'key'}${'name'}`]: 'value'
+  ["keyname"]: 'value'
 };
 console.log('Key exists:', Object.hasOwn(obj, 'keyname'));
 console.log('Value:', obj.keyname);
Key exists: true
Value: value
r1-p006 · __proto__ key handling affected by rewrite
1const protoObj = { __proto__: null };
2const key = `${'__proto__'}${'sub'}`;
3try {
4 protoObj[key] = 'should not set proto';
5 console.log('Assignment succeeded, proto unchanged:', protoObj.__proto__ === null);
6} catch (e) {
7 console.log('Error:', e.name, e.message);
8}
--- orig.js	2026-10-17 16:47:44.265221801 -0400
+++ fixed.js	2026-10-17 16:47:44.535228014 -0400
@@ -1,5 +1,5 @@
 const protoObj = { __proto__: null };
-const key = `${'__proto__'}${'sub'}`;
+const key = "__proto__sub";
 try {
   protoObj[key] = 'should not set proto';
   console.log('Assignment succeeded, proto unchanged:', protoObj.__proto__ === null);
Assignment succeeded, proto unchanged: false
r1-p007 · this binding affected in method extraction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-47-3)
1const obj = {
2 getKey() { return `${'prefix'}${'suffix'`; }
3};
4const fn = obj.getKey;
5console.log('Direct call:', obj.getKey());
6try {
7 console.log('Extracted call:', fn());
8} catch (e) {
9 console.log('Error:', e.name, e.message);
10}
r1-p008 · Exceptions in removed code change behavior (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-47-3)
1let threw = false;
2try {
3 const x = `${'ok'}${(function(){ throw new Error('side effect'); })()}${'fail'`;
4} catch (e) {
5 threw = true;
6 console.log('Caught:', e.name, e.message);
7}
8console.log('Threw:', threw);