> ## 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.

# string_concat_to_template_literal

> Convert "Hello " + name + "!" style concatenations to template literals (ES2015+).

**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

<CodeGroup>
  ```ts before.ts theme={null}
  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;
  ```

  ```ts after.ts theme={null}
  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}`;
  ```
</CodeGroup>

## 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).

<Note>
  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.
</Note>
