Skip to content

Add discriminatedUnion kind to V2RuntimeSchema coercion - #2801

Open
jar-stripe wants to merge 4 commits into
masterfrom
jar/discriminated-unions-node-coercion
Open

Add discriminatedUnion kind to V2RuntimeSchema coercion#2801
jar-stripe wants to merge 4 commits into
masterfrom
jar/discriminated-unions-node-coercion

Conversation

@jar-stripe

@jar-stripe jar-stripe commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Why?

The sdk-codegen now emits a discriminatedUnion schema kind for V2 methods whose request/response types contain discriminated unions with int64_string or decimal_string fields that need runtime coercion. The stripe-node runtime needs to handle this new schema kind.

What?

  • Adds {kind: 'discriminatedUnion', discriminator: string, variants: Record<string, V2RuntimeSchema>} to the V2RuntimeSchema type in Types.ts
  • Adds case 'discriminatedUnion': to both coerceV2RequestData and coerceV2ResponseData in V2Coercion.ts
  • Runtime behavior: inspects data[schema.discriminator], looks up the matching variant schema, and recursively coerces only that variant's fields. If the discriminator value isn't found or data doesn't have the field, returns data as-is.
  • Adds 16 tests covering matching variants, unknown/missing discriminator, null passthrough, and non-object graceful handling

Changelog

  • Adds runtime support for coercing int64/decimal fields within discriminated union variants in V2 API responses and requests

jar-stripe and others added 2 commits August 3, 2026 17:41
The codegen now emits a discriminatedUnion schema for V2 methods whose
request/response types contain discriminated unions with coercible fields.
This adds the runtime handling: inspect the discriminator field value,
look up the matching variant schema, and coerce only that variant's fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
@jar-stripe
jar-stripe marked this pull request as ready for review August 6, 2026 21:09
@jar-stripe
jar-stripe requested a review from a team as a code owner August 6, 2026 21:09
@jar-stripe
jar-stripe requested review from xavdid and a lite review from Copilot and removed request for a team August 6, 2026 21:09
Tests discriminated union type shapes for both request-side (params with
literal discriminator) and response-side (object fields), covering
standalone and inline variants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/V2Coercion.ts:153

  • discriminatorValue in schema.variants also returns true for properties inherited from Object.prototype (e.g. toString), which can cause schema.variants[discriminatorValue] to be a non-schema value and break coercion. Use an own-property check instead.
      const obj = data as Record<string, unknown>;
      const discriminatorValue = obj[schema.discriminator];
      if (
        typeof discriminatorValue === 'string' &&
        discriminatorValue in schema.variants
      ) {
        return coerceV2ResponseData(data, schema.variants[discriminatorValue]);
      }

src/V2Coercion.ts:70

  • discriminatorValue in schema.variants also returns true for properties inherited from Object.prototype (e.g. toString), which can cause schema.variants[discriminatorValue] to be a non-schema value and break coercion. Use an own-property check instead.

This issue also appears on line 146 of the same file.

      const discriminatorValue = obj[schema.discriminator];
      if (
        typeof discriminatorValue === 'string' &&
        discriminatorValue in schema.variants
      ) {
        return coerceV2RequestData(data, schema.variants[discriminatorValue]);
      }

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/V2Coercion.ts:152

  • Using the in operator here can match properties inherited from Object.prototype (e.g. toString, __proto__), which could cause an unintended variant match and then pass a non-schema value into coercion (leading to schema.kind being undefined and returning undefined). Prefer an own-property check on variants.
      if (
        typeof discriminatorValue === 'string' &&
        discriminatorValue in schema.variants
      ) {
        return coerceV2ResponseData(data, schema.variants[discriminatorValue]);

src/V2Coercion.ts:70

  • Using the in operator here can match properties inherited from Object.prototype (e.g. toString, __proto__), which could cause an unintended variant match and then pass a non-schema value into coercion (leading to schema.kind being undefined and returning undefined). Prefer an own-property check on variants.

This issue also appears on line 148 of the same file.

      if (
        typeof discriminatorValue === 'string' &&
        discriminatorValue in schema.variants
      ) {
        return coerceV2RequestData(data, schema.variants[discriminatorValue]);

test/DiscriminatedUnion.spec.ts:8

  • This comment claims these hand-written types "exactly match" what codegen emits, but the file doesn't reference generated types or schemas, so it can drift and become misleading. Consider softening the wording to avoid asserting exact equivalence.
// Type definitions mirroring the shapes the codegen produces for discriminated
// unions.  These are hand-written here so the test file is self-contained, but
// they exactly match the object-literal typedef shape the Node generator emits
// for each variant.

@xavdid xavdid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This could also be a good opportunity for type narrowing tests! (which we have already)

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.

3 participants