From 3bf0dbf0fcd8e8164ee80120720b94ac513cfbc9 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 27 Mar 2026 10:04:37 -0400 Subject: [PATCH 1/3] Do not interpolate non string values automatically --- .../src/config/config-interpolation.ts | 5 ++-- .../test/config/config-interpolation.test.ts | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/compiler/src/config/config-interpolation.ts b/packages/compiler/src/config/config-interpolation.ts index d96d8c4dd15..a0fe5364481 100644 --- a/packages/compiler/src/config/config-interpolation.ts +++ b/packages/compiler/src/config/config-interpolation.ts @@ -114,8 +114,9 @@ export function resolveValues>( resolvingValues.delete(keyPath); return value; } - const replaced = value.replace(VariableInterpolationRegex, (_, expression) => { - return (resolveExpression(expression) as string) ?? `{${expression}}`; + const replaced = value.replace(VariableInterpolationRegex, (match, expression) => { + const resolved = resolveExpression(expression); + return typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean" ? String(resolved) : match; }); resolvingValues.delete(keyPath); return replaced; diff --git a/packages/compiler/test/config/config-interpolation.test.ts b/packages/compiler/test/config/config-interpolation.test.ts index 6a639444ce7..f8c5d711252 100644 --- a/packages/compiler/test/config/config-interpolation.test.ts +++ b/packages/compiler/test/config/config-interpolation.test.ts @@ -133,6 +133,32 @@ describe("compiler: config interpolation", () => { }, ]); }); + + it("does not interpolate variables that resolve to non-string values", () => { + const [resolved, diagnostics] = resolveValues({ + "output-file": "openapi.{file-type}", + "file-type": ["yaml", "json"] as any, + }); + expectDiagnosticEmpty(diagnostics); + deepStrictEqual(resolved, { + "output-file": "openapi.{file-type}", + "file-type": ["yaml", "json"], + }); + }); + + it("interpolates number and boolean values", () => { + const [resolved, diagnostics] = resolveValues({ + path: "v{version}/debug-{debug}", + version: 3 as any, + debug: true as any, + }); + expectDiagnosticEmpty(diagnostics); + deepStrictEqual(resolved, { + path: "v3/debug-true", + version: 3, + debug: true, + }); + }); }); describe("expandConfigVariables", () => { From cab514195d345f0cacd44ccd9d47d6d68cf65188 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 27 Mar 2026 10:11:04 -0400 Subject: [PATCH 2/3] chg --- .../fix-file-type-interpolation-2026-2-27-10-9-43.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .chronus/changes/fix-file-type-interpolation-2026-2-27-10-9-43.md diff --git a/.chronus/changes/fix-file-type-interpolation-2026-2-27-10-9-43.md b/.chronus/changes/fix-file-type-interpolation-2026-2-27-10-9-43.md new file mode 100644 index 00000000000..081e574e862 --- /dev/null +++ b/.chronus/changes/fix-file-type-interpolation-2026-2-27-10-9-43.md @@ -0,0 +1,12 @@ +--- +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Do not interpolate non primitive values in config automatically + ```yaml + file-type: ["json", "yaml"] + output-file: "openapi.{file-type}" + ``` + Will not be interpolated as `openapi.json,yaml` but keep the placeholder `{file-type}` intact for the emitter to handle. From 590e8157005e5db58e35b1f4209fb0f8c5126ea2 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 27 Mar 2026 10:32:37 -0400 Subject: [PATCH 3/3] format --- packages/compiler/src/config/config-interpolation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/config/config-interpolation.ts b/packages/compiler/src/config/config-interpolation.ts index a0fe5364481..705cf2bce24 100644 --- a/packages/compiler/src/config/config-interpolation.ts +++ b/packages/compiler/src/config/config-interpolation.ts @@ -116,7 +116,11 @@ export function resolveValues>( } const replaced = value.replace(VariableInterpolationRegex, (match, expression) => { const resolved = resolveExpression(expression); - return typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean" ? String(resolved) : match; + return typeof resolved === "string" || + typeof resolved === "number" || + typeof resolved === "boolean" + ? String(resolved) + : match; }); resolvingValues.delete(keyPath); return replaced;