Skip to content

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

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

[Repo Assist] fix(js): handle .NET format specifiers in F# interpolated strings#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
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