react-client-sdk
react-client-sdk copied to clipboard
Allow users to create type-safe/strictly typed feature flags with useFlags
Requirements
- [ ] I have added test coverage for new or changed functionality
- [X] This is typing-only
- [X] I have followed the repository's pull request submission guidelines
- [X] I have validated my changes against all supported platform versions
Related issues
#139, https://github.com/launchdarkly/js-sdk-common/issues/32
Describe the solution you've provided
This MR updates useFlags generics and definition to allow implementing codebases to specify their own strictly typed feature flag interface.
// Before
declare const useFlags: <T extends LDFlagSet = LDFlagSet>() => T;
// After
declare function useFlags<T extends Record<string, any> = LDFlagSet>(): T;
By declaring useFlags as const and not a function it is not possible to overload its definition. Function overloads allow implementing codebases to re-declare useFlags to be more strict.
Additionally, currently the generic on useFlags is set to extends LDFlagSet, but this is not necessarily true. When useCamelCaseFlagKeys is true, the return value from useFlags can differ from LDFlagSet if LDFlagSet has been augmented for the client.
Describe alternatives you've considered
I also considered introducing a second interface, ReactLDFlagSet (or similar), which did not include an index type but this would not be backwards compatible and it seems like the goal is to have strict typing be opt-in.
Open to other alternatives, in our codebase we've considered writing a wrapping function.
Additional context
Example codebase implementation using these changes:
declare module 'launchdarkly-js-sdk-common' {
export interface LDFlagSet {
'show-a-cool-feature': boolean;
'demonstrate-another-cool-number': number;
}
}
const ldClient = initialize(
getClientSideID(),
{ anonymous: true },
{
allAttributesPrivate: true,
sendEvents: true,
},
);
// ldClient.allFlags()['demonstrate-another-cool-number']
export interface CamelCaseFeatureFlags {
showACoolFeature: boolean;
demonstrateAnotherCoolNumber: number;
}
declare module 'launchdarkly-react-client-sdk' {
export function useFlags(): CamelCaseFeatureFlags;
}
export const LaunchDarklyProvider: React.FC<unknown> = props => {
return (
<LDProvider
clientSideID={getClientSideID()}
ldClient={ldClient}
reactOptions={{
useCamelCaseFlagKeys: true,
}}
options={{
// bootstrap: defaultFeatureFlags,
}}
>
{props.children}
</LDProvider>
);
};
Hello @ewlsh, thank you for the contribution! We will discuss the change and give you a reply after that.
@yusinto this is updated.