diff --git a/packages/compiler/src/lib/paging.ts b/packages/compiler/src/lib/paging.ts index f0ed07f32c5..61b97ae646f 100644 --- a/packages/compiler/src/lib/paging.ts +++ b/packages/compiler/src/lib/paging.ts @@ -26,7 +26,7 @@ import type { } from "../core/types.js"; import { createStateSymbol } from "../lib/utils.js"; import { DuplicateTracker, useStateSet } from "../utils/index.js"; -import { isNumericType, isStringType } from "./decorators.js"; +import { isNumericType } from "./decorators.js"; export const [ /** @@ -109,18 +109,7 @@ export const [ markContinuationTokenProperty, /** {@inheritdoc ContinuationTokenDecorator} */ continuationTokenDecorator, -] = createMarkerDecorator("continuationToken", (context, target) => { - if (!isStringType(context.program, target.type)) { - reportDiagnostic(context.program, { - code: "decorator-wrong-target", - messageId: "withExpected", - format: { decorator: "continuationToken", expected: "string", to: getTypeName(target.type) }, - target: context.decoratorTarget, - }); - return false; - } - return true; -}); +] = createMarkerDecorator("continuationToken"); export const [ /** diff --git a/packages/compiler/test/decorators/paging.test.ts b/packages/compiler/test/decorators/paging.test.ts index 6174445cd30..e4ab7d9b9c5 100644 --- a/packages/compiler/test/decorators/paging.test.ts +++ b/packages/compiler/test/decorators/paging.test.ts @@ -213,3 +213,80 @@ describe("collect nested paging properties", () => { expect(pathString).toBe("results.items"); }); }); + +describe("@continuationToken supports nullable and optional properties", () => { + it("accepts nullable string type (string | null)", async () => { + const diagnostics = await runner.diagnose(` + @list op list(): { + @pageItems items: string[]; + @continuationToken token: string | null; + }; + `); + expectDiagnosticEmpty(diagnostics); + }); + + it("accepts optional string type", async () => { + const diagnostics = await runner.diagnose(` + @list op list(): { + @pageItems items: string[]; + @continuationToken token?: string; + }; + `); + expectDiagnosticEmpty(diagnostics); + }); + + it("accepts nullable optional string type", async () => { + const diagnostics = await runner.diagnose(` + @list op list(): { + @pageItems items: string[]; + @continuationToken token?: string | null; + }; + `); + expectDiagnosticEmpty(diagnostics); + }); + + it("accepts non-string types", async () => { + const diagnostics = await runner.diagnose(` + @list op list(): { + @pageItems items: string[]; + @continuationToken token: int32; + }; + `); + expectDiagnosticEmpty(diagnostics); + }); + + it("collects nullable continuation token in input", async () => { + const { list, token } = (await runner.compile(` + @list @test op list( + @continuationToken @test token: string | null + ): { @pageItems items: string[] }; + `)) as { list: Operation; token: ModelProperty }; + + const paging = ignoreDiagnostics(getPagingOperation(runner.program, list)); + expect(paging?.input.continuationToken?.property).toBe(token); + }); + + it("collects nullable continuation token in output", async () => { + const { list, token } = (await runner.compile(` + @list @test op list(): { + @pageItems items: string[]; + @continuationToken @test token: string | null; + }; + `)) as { list: Operation; token: ModelProperty }; + + const paging = ignoreDiagnostics(getPagingOperation(runner.program, list)); + expect(paging?.output.continuationToken?.property).toBe(token); + }); + + it("collects optional continuation token", async () => { + const { list, token } = (await runner.compile(` + @list @test op list(): { + @pageItems items: string[]; + @continuationToken @test token?: string; + }; + `)) as { list: Operation; token: ModelProperty }; + + const paging = ignoreDiagnostics(getPagingOperation(runner.program, list)); + expect(paging?.output.continuationToken?.property).toBe(token); + }); +});