Skip to content

fix(js): handle .NET format specifiers in F# interpolated strings [Repo Assist] #4553

Draft
github-actions[bot] wants to merge 1 commit intomainfrom
repo-assist/fix-interpolated-string-dotnet-format-specifiers-090b3e028080934e
Draft

fix(js): handle .NET format specifiers in F# interpolated strings [Repo Assist] #4553
github-actions[bot] wants to merge 1 commit intomainfrom
repo-assist/fix-interpolated-string-dotnet-format-specifiers-090b3e028080934e

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

🤖 This is an automated pull request from Repo Assist, an AI-powered assistant.

Closes #4046 (items 3 and 4 — $"{x:N0}" and $"{n:#,#} items" producing literal %P(N0) / %P(#,#) output).


Root cause

When an F# interpolated string uses a .NET format specifier such as $"{x:N0}", the F# compiler encodes the hole in the PrintFormat format string as %P(N0) — with the specifier inside the parentheses.

The regex in makeStringTemplateFromWith (Replacements.Util.fs) was %P\(\), which only matched empty parentheses. The pattern %P(N0) was never recognised as a hole, so:

  1. makeStringTemplateFrom incorrectly built a StringTemplate that contained the literal text %P(N0) with no interpolated value — a silent, wrong result.
  2. makeStringTemplateFromAllowingFormat (the second attempt) never got a chance to apply formatting because the first attempt returned Some(brokenTemplate) instead of None.

The end result was the literal string %P(N0) appearing in the JavaScript output.

Fix

  • src/Fable.Transforms/Replacements.Util.fs

    • Update the regex from %P\(\) to %P\(([^)]*)\) to also capture the optional .NET specifier inside the parens (captured in Group 2).
    • Add a handleDotNetSpec: string -> Expr -> Expr option callback parameter to the private makeStringTemplateFromWith.
    • makeStringTemplateFrom passes (fun _ _ -> None) for handleDotNetSpec, so it correctly returns None when a .NET spec is present, falling through to makeStringTemplateFromAllowingFormat.
    • makeStringTemplateFromAllowingFormat passes a callback that calls String.format("{0:<spec>}", value) — the same library function that already handles (1000).ToString("N0").
  • tests/Js/Main/StringTests.fs

    • Two new test cases covering N0, N2, F2 standard numeric specifiers and the #,# custom pattern, including mixed strings like $"Count: {n:N0} items".

Trade-offs

  • Python, Dart, and BEAM targets still fall through to their runtime String.interpolate for these cases (which also doesn't handle .NET specs) — that is a pre-existing limitation and left for a separate fix. This PR only targets JavaScript/TypeScript.
  • No changes to src/Fable.AST/ or src/Fable.Core/.
  • No changelog update — the changelog is auto-generated from commit history per project convention.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@51c8f6ad4357d2ecc06e47120031b3d75e80227d

When an F# interpolated string uses a .NET format specifier
(e.g. `$"{x:N0}"` or `$"{n:#,#} items"`), the F# compiler
encodes the hole as `%P(N0)` in the PrintFormat format string
(with a non-empty specifier inside the parens).

The previous regex `%P\(\)` only matched empty parens, so `%P(N0)`
was never recognised as a hole.  This caused `makeStringTemplateFrom`
to silently return a broken StringTemplate that included the literal
text `%P(N0)` with no interpolated value.

Fix:
- Update the regex in `makeStringTemplateFromWith` to
  `%P\(([^)]*)\)` so it also captures non-empty .NET format
  specifiers.
- Add a `handleDotNetSpec` callback alongside the existing
  `handleFormatSpec` callback.
- In `makeStringTemplateFrom`, return None when a .NET spec is
  encountered (correctly falls through to AllowingFormat).
- In `makeStringTemplateFromAllowingFormat`, wrap the value in
  `String.format("{0:<spec>}", value)` so the specifier is
  applied at runtime.

Closes #4046 (items 3 and 4)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added automation Automated changes bug javascript repo-assist Created by Repo Assist labels Apr 22, 2026
@dbrattli dbrattli changed the title [Repo Assist] fix(js): handle .NET format specifiers in F# interpolated strings fix(js): handle .NET format specifiers in F# interpolated strings [Repo Assist] May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation Automated changes bug javascript repo-assist Created by Repo Assist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Numeric format strings silently ignoring unsupported values or producing wrong results

0 participants