Skip to content

fix(sdk-codegen): make run_inline_query response type generic#1730

Open
marc-wilson wants to merge 2 commits into
looker-open-source:mainfrom
marc-wilson:fix/run-inline-query-json-type
Open

fix(sdk-codegen): make run_inline_query response type generic#1730
marc-wilson wants to merge 2 commits into
looker-open-source:mainfrom
marc-wilson:fix/run-inline-query-json-type

Conversation

@marc-wilson

Copy link
Copy Markdown

What

run_inline_query is hardcoded to return SDKResponse<string, ...>, but when result_format is 'json' the transport layer actually deserializes the body into an object/array. Since value is typed as string, callers have no valid TypeScript path to the real data and are forced into as unknown as T casts, which defeats type safety entirely.

Looker's OpenAPI spec declares a single 200: { schema: { type: string } } response for this endpoint with no per-result_format breakdown, so the generator can't derive the real shape per format automatically. This PR makes run_inline_query generic instead:

async run_inline_query<T = string>(
  request: IRequestRunInlineQuery,
  options?: Partial<ITransportSettings>
): Promise<SDKResponse<T, IError | IValidationError>>

Callers who know their query's shape (they always do — they specify model/view/fields) can now write:

const response = await sdk.run_inline_query<StoreProduct[]>(req);
if (response.ok) {
  response.value; // StoreProduct[] — no cast
}

Backward compatibility — this is opt-in, no existing consumers break

  • <T = string> has a default type argument. Any existing call site that doesn't supply a generic (sdk.run_inline_query(request)) resolves T to string, exactly as before — no change in inferred type, no change in behavior.
  • Generic type parameters are compile-time only and erased by the TypeScript compiler; the emitted JavaScript for this.post(...) is identical regardless of T. No runtime behavior changes.
  • The method signature (arguments, arity, return shape) is otherwise untouched.
  • Verified: yarn build type-checks the full monorepo against the new signature with zero errors, and reverting only the implementation (leaving the generic interface) reliably produces a type error — confirming the generic threads correctly end-to-end and that plain, non-generic call sites are unaffected.

Scope

This is scoped to run_inline_query only, per the linked issue. The same result_format-driven typing problem exists on a few sibling methods (run_query, run_look, run_url_encoded_query, run_sql_query) that weren't touched here to keep this PR minimal and reviewable. Happy to circle back and apply the same generic-default pattern to those once this approach is confirmed to be the right one.

Changes

  • packages/sdk-codegen/src/typescript.gen.ts: generator now emits <T = string> for methods special-cased as "format-dependent string" responses (currently just run_inline_query).
  • packages/sdk/src/4.0/methods.ts, methodsInterface.ts, funcs.ts: regenerated output for run_inline_query reflecting the generic.

Fixes #1703

Test plan

  • yarn build passes cleanly across all 21 packages
  • Full sdk / sdk-codegen / sdk-rtl jest suites pass (only pre-existing, unrelated failures remain — 2 tests requiring a live looker.ini, confirmed to fail identically on main)
  • Manually verified (via a temporary local test, not included in this PR) that a caller can do sdk.run_inline_query<MyType[]>(request) and get a fully typed response.value with no cast, and that omitting the generic still defaults to string
  • Confirmed the fix is load-bearing by reverting just the methods.ts implementation and re-running tsc — it immediately fails type-checking against the generic interface, proving the change is meaningful

`run_inline_query` was hardcoded to return `SDKResponse<string, ...>`,
but when `result_format` is 'json' the transport layer actually
deserializes the body into an object/array, forcing every JSON
consumer into `as unknown as T` casts. The Looker API spec declares a
single `string` response for this endpoint regardless of
`result_format`, so the generator can't derive the real per-format
shape automatically.

`run_inline_query` is now generic (`<T = string>`), so callers can
opt in to a typed result (`sdk.run_inline_query<MyType[]>(request)`)
with no cast. The default type argument keeps the signature fully
backward compatible: existing call sites that don't supply a generic
still resolve to `string`, identical to before.

Fixes looker-open-source#1703

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for generic response types in the TypeScript SDK generator for methods whose runtime return type depends on the requested format (such as run_inline_query). This is achieved by introducing helper methods in the generator to inject generic type parameters (<T = string>) and dynamically resolve the response type name. The reviewer suggested extending this generic response type support to streaming methods as well, ensuring that their callback signatures also benefit from the generic type instead of being hardcoded to string.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 453 to +454
const callback = `callback: (response: Response) => Promise<${mapped.name}>,`;
const generics = streamer ? '' : this.methodGenericParams(method);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Currently, if streamer is true, the generic type parameter is omitted (generics is set to '') and the callback return type is hardcoded to Promise<${mapped.name}> (which resolves to Promise<string>).

To ensure that streaming versions of format-dependent methods also benefit from the generic response type, we should use this.responseTypeName(method) in the callback signature and allow generics to be generated for streaming methods as well.

Suggested change
const callback = `callback: (response: Response) => Promise<${mapped.name}>,`;
const generics = streamer ? '' : this.methodGenericParams(method);
const callback = 'callback: (response: Response) => Promise<' + this.responseTypeName(method) + '>,';
const generics = this.methodGenericParams(method);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and agreed in principle — but I'd like to keep this specific to the non-streaming path for now.

The streaming variant's callback receives the raw Response/Readable (e.g. for piping a download to a file), so its return type is really "whatever the caller's callback resolves to," not the parsed response.value shape that issue #1703 is about. Hardcoding that callback's return type to string predates this PR and isn't something this change touches — I only added the generic to the non-streaming (streamer === false) branch.

That said, I think your suggestion is a reasonable extension: expose the same <T> on the streaming signature so callers of sdk.stream.run_inline_query<T>(...) aren't stuck with Promise<string> either. I'd rather do that as a deliberate follow-up (along with the other result_format-driven methods called out in the PR description — run_query, run_look, etc.) once the core generic-default pattern here is confirmed to be the right approach, rather than widen this PR's diff. Let me know if you'd prefer it bundled in now instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

run_inline_query returns a parsed object, not a string

1 participant