Skip to content

feat: support type-safe variant values for InMemoryProvider#1356

Open
MattIPv4 wants to merge 4 commits intoopen-feature:mainfrom
MattIPv4:MattIPv4/type-safe-in-memory-provider
Open

feat: support type-safe variant values for InMemoryProvider#1356
MattIPv4 wants to merge 4 commits intoopen-feature:mainfrom
MattIPv4:MattIPv4/type-safe-in-memory-provider

Conversation

@MattIPv4
Copy link
Member

@MattIPv4 MattIPv4 commented Feb 21, 2026

This PR

Applies a few generics to the Flag and FlagConfiguration types used by InMemoryProvider to allow for type-safe variant values, ensuring the defaultVariant and any value returned by contextEvaluator are present as keys in variants for the given flag.

Related Issues

Resolves #967

Closes #1046

Notes

If folks are passing in an object reference to InMemoryProvider, like we are in the specs, rather than an object literal directly, this is technically a breaking change as they'll need to as const or pass it directly, to allow TypeScript to correctly infer the types. I'm personally of the opinion that this isn't a breaking change in the API of the package, and so this can ship as a minor change, but open to being told this should be considered breaking.

Also, the monorepo is using a very old version of TypeScript, 4.4, so I've had to include a util for NoInfer rather than using the built-in util that's available in TypeScript 5.4 and beyond.

Follow-up Tasks

N/A

How to test

See specs where this is already demonstrated to be working as I've had to apply @ts-expect-error when we're using invalid variant keys.

@MattIPv4 MattIPv4 requested review from a team as code owners February 21, 2026 02:56
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MattIPv4, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the type safety of the InMemoryProvider by introducing generics to its core configuration types. The primary goal is to prevent runtime errors by ensuring that flag variant keys are consistently validated at compile-time, improving the reliability and developer experience when defining feature flags. This change makes the InMemoryProvider more robust and easier to use with strong typing.

Highlights

  • Type-Safe Variant Values: Introduced generics to the Flag and FlagConfiguration types within InMemoryProvider to ensure type-safe variant values. This guarantees that defaultVariant and any value returned by contextEvaluator are valid keys within the variants object for a given flag.
  • NoInfer Utility Type: Added a NoInfer utility type to maintain compatibility with older TypeScript versions (specifically 4.4, as used in the monorepo), as the built-in NoInfer is only available from TypeScript 5.4 onwards.
  • Potential Breaking Change for Object Literals: Noted a potential breaking change for consumers passing existing objects to InMemoryProvider configurations. These objects may now require an as const assertion to allow TypeScript to correctly infer the types and leverage the new type safety.
  • Enhanced Test Coverage: Updated existing tests to reflect the new type-safe behavior, including the strategic use of as const assertions and @ts-expect-error comments to validate both correct and intentionally incorrect type scenarios.
Changelog
  • packages/web/src/provider/in-memory-provider/flag-configuration.ts
    • Introduced a NoInfer utility type for TypeScript version compatibility.
    • Defined a new FlagVariants type to represent type-safe variant records.
    • Updated the Flag type to be generic, accepting a type parameter T for variant keys, and utilized FlagVariants and NoInfer.
    • Modified the FlagConfiguration type to be generic, enforcing type safety across flag definitions.
  • packages/web/src/provider/in-memory-provider/in-memory-provider.ts
    • Made the InMemoryProvider class generic to accept a type parameter T for its flag configuration.
    • Updated the constructor and putConfiguration method signatures to use the new generic FlagConfiguration type, propagating type safety throughout the provider.
  • packages/web/test/in-memory-provider.spec.ts
    • Applied as const assertions to flag configuration objects in various test cases to enable stricter type inference.
    • Added @ts-expect-error comments to specific test lines where defaultVariant or contextEvaluator are intentionally set to invalid variant keys, verifying the new type-checking behavior.
    • Corrected casing for StringFlagSpec to stringFlagSpec in multiple test declarations for consistency.
Activity
  • MattIPv4 created this pull request to add type-safe variant values for the InMemoryProvider.
  • The changes address and resolve issue Type safety of Variant keys #967, which likely pertained to type safety concerns with flag variants.
  • The author noted that existing InMemoryProvider consumers passing objects directly might need to add as const for proper type inference, but considers it a minor change.
  • A custom NoInfer utility type was implemented due to the monorepo's use of an older TypeScript version (4.4), which lacks the built-in NoInfer.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@MattIPv4 MattIPv4 force-pushed the MattIPv4/type-safe-in-memory-provider branch from aa6c00f to e2caf7d Compare February 21, 2026 02:56
Copy link
Contributor

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

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 generics to the InMemoryProvider and its associated flag configuration types to enable type-safe variant values. This is a significant improvement, ensuring that defaultVariant and the return values from contextEvaluator are valid keys within the variants object. The implementation leverages advanced TypeScript features effectively, and the accompanying test updates are thorough, correctly using as const and @ts-expect-error to validate the new type safety. Overall, this is a well-executed feature enhancement.

Signed-off-by: MattIPv4 <me@mattcowley.co.uk>
Signed-off-by: MattIPv4 <me@mattcowley.co.uk>
Signed-off-by: MattIPv4 <me@mattcowley.co.uk>
Signed-off-by: MattIPv4 <me@mattcowley.co.uk>
@MattIPv4 MattIPv4 force-pushed the MattIPv4/type-safe-in-memory-provider branch from 0e7d325 to 0b723eb Compare February 21, 2026 03:13
Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

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

Hey @MattIPv4, thanks for the PR. It's nice a great improvement to the in memory provider. However, it appears to be a slight breaking change. Is this expected?

Type error

	const booleanFlagSpec = {
        'a-boolean-flag': {
          variants: {
            on: true,
            off: false,
          },
          disabled: false,
          defaultVariant: 'on',
        },
      }
      await provider.putConfiguration(booleanFlagSpec);
                                                               ^ Type 'string' is not assignable to type '"on" | "off"'.ts(2345)

No type error

Type error

	const booleanFlagSpec = {
        'a-boolean-flag': {
          variants: {
            on: true,
            off: false,
          },
          disabled: false,
          defaultVariant: 'on',
        },
      } as const;
      await provider.putConfiguration(booleanFlagSpec);

I understand why the const is important to the types but it would be nice if it fell back to the non-type safe version if the definition isn't a readonly type.

@MattIPv4
Copy link
Member Author

Yeah, as I said in the notes, that is expected as a "breaking" change, though my view would be that it isn't actually a breaking change to the API that the SDK offers, it is simple enough for folks to add as const or pass a literal directly?

I'm not sure I'm keen on the idea of having it fall back to not being type safe if it cannot infer correctly, that feels like it'd end up being a DX trap for folks thinking it is acting in a type-safe way when actually it isn't because it silently failed to infer the types it needed?

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.

Type safety of Variant keys

2 participants