Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.refactron.dev/llms.txt

Use this file to discover all available pages before exploring further.

Transform ID: string_concat_to_template_literal Language: TypeScript

What it does

Rewrites +-based string concatenations to template literals. Both left-leaning and right-leaning trees flatten to the same operand list, so "a" + b + "c" and "a" + (b + "c") both become `a${b}c`. Backticks (`) and literal ${ digraphs in the source are faithfully escaped so the resulting template parses identically. Type safety is enforced via ts-morph’s getType() on every non-string-literal operand: only string / number / boolean (and unions of those) are allowed. Anything any, unknown, or non-primitive refuses cleanly — we never paper over an accidental + (addition) that happens to involve a string somewhere. Transform at src/transform/transforms/typescript/string-concat-to-template-literal.ts.

Detector pattern

The detector at src/analyze/detectors/typescript/string-concat.ts flags top-level +-binary chains (the outermost + in a sequence of nested +-binaries) where at least one operand is a string literal.

Preconditions

  1. non_es2015 — refuses when the resolved tsconfig target predates ES2015. Template literals aren’t in the runtime grammar before ES2015.
  2. unknown_operand_type — refuses when any operand has type any or unknown. Could be addition, could be concatenation — we don’t guess.
  3. non_coercible_operand — refuses when any operand has a non-primitive type (object, array, …). Coercing via implicit toString() is rarely intentional.
  4. At least one operand in the chain is a string literal ("…" or `…` no-substitution template). Pure-numeric a + b + c is left alone.

Before / after

const name: string = 'world';
const n: number = 42;

const greeting = 'Hello ' + name + '!';
const stat = 'n=' + n;
const escaped = 'It is `tick`: ' + name;
const moneyShape = 'money ${USD}: ' + name;

Edge cases NOT handled (skip via precondition)

  • any / unknown operands — unknown_operand_type. The + could be addition.
  • Non-primitive operands (objects, arrays, custom classes) — non_coercible_operand. toString() coercion is almost never intended.
  • tsconfig target < ES2015 — non_es2015; template literals aren’t available.
  • Pure-numeric chains with no string operand — not a concatenation; left alone.
  • A +-chain at a sub-expression position whose parent is also a +-binary — only the outermost chain is rewritten (the inner one flattens into its operand list).
The type checks are intentionally conservative: a chain with even one untyped operand refuses outright. This costs a few legitimate rewrites in untyped code, but eliminates the risk of converting an arithmetic + into a string-coercion bug.