Skip to content

fix(browser): Accept precisely-typed GrowthBook class in growthbookIntegration#21825

Open
codr wants to merge 1 commit into
getsentry:developfrom
codr:fix/growthbook-integration-class-type
Open

fix(browser): Accept precisely-typed GrowthBook class in growthbookIntegration#21825
codr wants to merge 1 commit into
getsentry:developfrom
codr:fix/growthbook-integration-class-type

Conversation

@codr

@codr codr commented Jun 27, 2026

Copy link
Copy Markdown

Summary

growthbookIntegration({ growthbookClass: GrowthBook }) — the exact call shown in the integration's own docs/JSDoc — does not type-check against the real @growthbook/growthbook GrowthBook class, forcing consumers to cast.

The parameter was typed as a constructor with unknown[] args:

export type GrowthBookClass = new (...args: unknown[]) => GrowthBook;        // @sentry/browser
export type GrowthBookClassLike = new (...args: unknown[]) => GrowthBookLike; // @sentry/core

GrowthBook's real constructor is constructor(options?: Options). A constructor with a narrow parameter is not assignable to one declared with (...args: unknown[]) (constructor parameters are contravariant: unknown is not assignable to Options), so typeof GrowthBook is rejected.

@sentry/core masks this by annotating the export as IntegrationFn ((...rest: any[])), but @sentry/browser re-declares the wrapper with the strict { growthbookClass: GrowthBookClass } parameter via satisfies IntegrationFn, which preserves the strict type — so the public browser/react API requires a cast.

Fix

The integration only ever reads growthbookClass.prototype to monkey-patch isOn / getFeatureValue; it never constructs the class. Typing the parameter as exactly that:

export type GrowthBookClass = { prototype: GrowthBook };
export type GrowthBookClassLike = { prototype: GrowthBookLike };

accepts any class whose instances expose the wrapped surface (so typeof GrowthBook works with no cast), still rejects classes missing isOn / getFeatureValue, and avoids any. It also makes the internal growthbookClass.prototype as GrowthBookLike cast redundant, which is removed.

Test

Adds packages/browser/test/integrations/featureFlags/growthbook/integration.test.ts: passes a mock class with a real, narrow constructor to growthbookIntegration with no cast (the type regression guard) and asserts boolean evaluations are captured to the scope's flag context.


  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).
  • No related issue is linked — happy to file one if preferred.

…tegration

`growthbookIntegration({ growthbookClass: GrowthBook })` — the call shown in the
integration's own docs — did not type-check against the real
`@growthbook/growthbook` class, forcing consumers to cast. The parameter was
typed as `new (...args: unknown[]) => GrowthBook`, and a narrow constructor
(`constructor(options?: Options)`) is not assignable to an `unknown[]`
constructor.

The integration only reads `growthbookClass.prototype`, so type the parameter
structurally as `{ prototype: GrowthBook }`. This accepts a real GrowthBook
class with no cast, still rejects classes missing `isOn`/`getFeatureValue`, uses
no `any`, and makes the internal prototype cast redundant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codr codr requested a review from a team as a code owner June 27, 2026 03:58
@codr codr requested review from Lms24, logaretm and mydea and removed request for a team June 27, 2026 03:58
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.

1 participant