Skip to content

Add treat-unknown-as-record feature flag to map TypeSpec unknown to Record<string, unknown> in Modular SDK#3869

Merged
MaryGao merged 5 commits intomainfrom
copilot/review-emitter-typespec-generation
Mar 30, 2026
Merged

Add treat-unknown-as-record feature flag to map TypeSpec unknown to Record<string, unknown> in Modular SDK#3869
MaryGao merged 5 commits intomainfrom
copilot/review-emitter-typespec-generation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 26, 2026

When migrating from HLC to TypeSpec-generated Modular SDKs, TypeSpec unknown emits as any, but HLC mapped the equivalent swagger type to Record<string, unknown>. This breaks public API compatibility for migrating services.

Changes

  • New emitter option treat-unknown-as-record (boolean, Modular only, default false): when enabled, TypeSpec unknown maps to Record<string, unknown> instead of any
  • Type resolution (get-type-expression.ts): unknown case now checks context.rlcOptions?.treatUnknownAsRecord to select the output type
  • No-wrap behavior (operationHelpers.ts): checkWrapNonModelReturn now returns noWrap for unknown type when treatUnknownAsRecord is enabled, treating it as a Dictionary/Record type (consistent with HLC's PropertyKind.Dictionary → no body wrapper behavior)
  • Option wiring: threaded through EmitterOptionsRLCOptionsSchemaRLCOptionstransfromRLCOptions.ts following the existing pattern used by similar compat flags
  • Test utilities: emitModularModelsFromTypeSpec and emitModularOperationsFromTypeSpec wired to forward the new option
  • Scenario tests: added treatUnknownAsRecord.md covering enabled/disabled flag behavior and the noWrap interaction with wrap-non-model-return

Example

With treat-unknown-as-record: true in tspconfig.yaml:

// Before (default)
export interface Foo {
  bar: any;
  baz: any[];
}

// After (treat-unknown-as-record: true)
export interface Foo {
  bar: Record<string, unknown>;
  baz: Record<string, unknown>[];
}

When combined with wrap-non-model-return: true, the unknown return type is no longer wrapped in a { body: any } alias — it is returned directly as Record<string, unknown>:

// Before (wrap-non-model-return: true, treat-unknown-as-record not set)
export async function getAny(...): Promise<GetAnyResponse> { ... }
// GetAnyResponse = { body: any }

// After (both flags enabled)
export async function getAny(...): Promise<Record<string, unknown>> { ... }

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI changed the title [WIP] Review comment on emitter type generation from unknown to any Add treat-unknown-as-record feature flag to map TypeSpec unknown to Record<string, unknown> in Modular SDK Mar 26, 2026
Copilot AI requested a review from MaryGao March 26, 2026 09:15
return getEnumExpression(context, type);
case "unknown":
return "any";
return context.rlcOptions?.treatUnknownAsRecord
Copy link
Copy Markdown
Member

@MaryGao MaryGao Mar 27, 2026

Choose a reason for hiding this comment

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

@copilot when the treatUnknownAsRecord is enabled, we should generate noWrap for unknown type. also add an UT for unknow as return type for treatUnknownAsRecord on/off.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 6d87a9f. When treatUnknownAsRecord is enabled, checkWrapNonModelReturn now returns noWrap for unknown type (treating it as a Dictionary/Record type, consistent with HLC's PropertyKind.Dictionary → no body wrapper behavior). The return type is Record<string, unknown> directly instead of being wrapped in { body: any }.

@MaryGao MaryGao marked this pull request as ready for review March 27, 2026 05:52
Copilot AI requested a review from MaryGao March 27, 2026 06:01
@MaryGao MaryGao merged commit a177ba3 into main Mar 30, 2026
16 checks passed
@MaryGao MaryGao deleted the copilot/review-emitter-typespec-generation branch March 30, 2026 05:45
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.

4 participants