Skip to content
This repository was archived by the owner on Jun 30, 2026. It is now read-only.

Commit e35c324

Browse files
maorlegerCopilot
andcommitted
fix(typespec-ts): preserve optional api-version on client context
The rewritten client-context renderer was treating every api-version parameter as required based on isApiVersion alone, even when the adapter had already recorded a client default and left the parameter optional. That changed generated *Context interfaces from apiVersion?: string to apiVersion: string for defaulted clients. Make requiredness follow the adapted TSClientParameter metadata instead of the api-version marker, and lock it in with a modular unit test that renders the new pipeline's client context for a versioned client with a defaulted api-version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d24c617 commit e35c324

2 files changed

Lines changed: 43 additions & 7 deletions

File tree

packages/typespec-ts/src/codegen/clients.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,7 @@ export function emitClientContext(
5252

5353
// ── Client interface ──
5454
const requiredProperties = client.parameters
55-
.filter(
56-
(p) =>
57-
!p.isEndpoint &&
58-
!p.isCredential &&
59-
(p.isApiVersion || (p.required && !p.hasDefaultValue))
60-
)
55+
.filter((p) => !p.isEndpoint && !p.isCredential && p.required)
6156
.map((p) => ({
6257
name: p.name,
6358
type: p.type,
@@ -331,7 +326,7 @@ function emitReturnStatement(
331326
!p.isEndpoint &&
332327
!p.isCredential &&
333328
p.name !== "options" &&
334-
(p.isApiVersion || (p.required && !p.hasDefaultValue))
329+
p.required
335330
);
336331

337332
const requiredParamNames = new Set(

packages/typespec-ts/test/modularUnit/adapter.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { describe, expect, it } from "vitest";
2+
import { Project } from "ts-morph";
23

4+
import { emitClientContext } from "../../src/codegen/clients.js";
5+
import { provideBinder } from "../../src/framework/hooks/binder.js";
36
import { renameClientName } from "../../src/index.js";
47
import { transformModularEmitterOptions } from "../../src/modular/buildModularOptions.js";
58
import {
@@ -373,6 +376,44 @@ describe("tcgc adapter", () => {
373376
);
374377
});
375378

379+
it("keeps client-default api-version optional on emitted context interfaces", async () => {
380+
const model = await adaptCodeModelFromTypeSpec(
381+
buildServiceTypeSpec(
382+
`
383+
enum Versions {
384+
v2026_05_15: "2026-05-15"
385+
}
386+
387+
model ApiVersionParameter {
388+
@query
389+
"api-version": string;
390+
}
391+
392+
@route("/widgets")
393+
op ping(...ApiVersionParameter): void;
394+
`,
395+
`
396+
@versioned(Versions)
397+
@server("{endpoint}/widgets", "Widgets", {
398+
endpoint: url
399+
})
400+
`
401+
)
402+
);
403+
404+
const client = model.clients[0]!;
405+
const apiVersion = client.parameters.find((parameter) => parameter.isApiVersion);
406+
expect(apiVersion).toMatchObject({ required: false, hasDefaultValue: true });
407+
408+
const project = new Project({ useInMemoryFileSystem: true });
409+
const binder = provideBinder(project);
410+
const file = emitClientContext(project, client, model.settings);
411+
binder.resolveAllReferences("");
412+
413+
expect(file?.getFullText()).toContain("apiVersion?: string;");
414+
expect(file?.getFullText()).not.toContain("apiVersion: string;");
415+
});
416+
376417
it("groups nested operations when operation groups are enabled", async () => {
377418
const client = await adaptFirstClientFromTypeSpec(
378419
buildServiceTypeSpec(`

0 commit comments

Comments
 (0)