From 99c9659e857ad862f6273fe2209abdd47073440c Mon Sep 17 00:00:00 2001
From: "andreas.karlsson" <andreas.karlsson.se@gmail.com>
Date: Thu, 22 Aug 2024 17:49:25 +0200
Subject: [PATCH] refactor: remove the widen type (#193)

* refactor: remove the widen type

* fixup! refactor: remove the widen type
---
 api/react.api.md               | 62 ++++++++++++++++++++++++++++++----
 api/sdk.api.md                 | 27 ++++++++++++---
 packages/react/src/index.tsx   | 52 ++++++++++++++++++++++++----
 packages/sdk/src/Confidence.ts | 16 ++++++---
 packages/sdk/src/Value.ts      |  9 -----
 packages/sdk/src/flags.ts      | 18 ++++++++--
 6 files changed, 151 insertions(+), 33 deletions(-)

diff --git a/api/react.api.md b/api/react.api.md
index b6093279..3fd87b8c 100644
--- a/api/react.api.md
+++ b/api/react.api.md
@@ -40,9 +40,21 @@ export class ConfidenceReact implements EventSender, Trackable, FlagResolver {
     get contextState(): string;
     // @internal
     readonly delegate: Confidence;
-    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
+    evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+    // (undocumented)
+    evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+    // (undocumented)
+    evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+    // (undocumented)
+    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
     getContext(): Context;
-    getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
+    getFlag(path: string, defaultValue: string): Promise<string>;
+    // (undocumented)
+    getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+    // (undocumented)
+    getFlag(path: string, defaultValue: number): Promise<number>;
+    // (undocumented)
+    getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
     setContext(context: Context, { transition }?: {
         transition?: boolean | undefined;
     }): void;
@@ -50,8 +62,20 @@ export class ConfidenceReact implements EventSender, Trackable, FlagResolver {
     track(name: string, message?: Value.Struct): void;
     track(manager: Trackable.Manager): Closer;
     useContext(): Context;
-    useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
-    useFlag<T extends Value>(path: string, defaultValue: T): Value.Widen<T>;
+    useEvaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+    // (undocumented)
+    useEvaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+    // (undocumented)
+    useEvaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+    // (undocumented)
+    useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
+    useFlag(path: string, defaultValue: string): string;
+    // (undocumented)
+    useFlag(path: string, defaultValue: number): number;
+    // (undocumented)
+    useFlag(path: string, defaultValue: boolean): boolean;
+    // (undocumented)
+    useFlag<T extends Value>(path: string, defaultValue: T): T;
     useWithContext(context: Context): ConfidenceReact;
     withContext(context: Context): ConfidenceReact;
 }
@@ -62,11 +86,37 @@ export const useConfidence: () => ConfidenceReact;
 // @public
 export function useConfidenceContext(confidence?: ConfidenceReact): Context;
 
+// Warning: (ae-missing-release-tag) "useEvaluateFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-missing-release-tag) "useEvaluateFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-missing-release-tag) "useEvaluateFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
 // @public
-export function useEvaluateFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): FlagEvaluation<Value.Widen<T>>;
+export function useEvaluateFlag(path: string, defaultValue: string, confidence?: ConfidenceReact): FlagEvaluation<string>;
 
+// @public (undocumented)
+export function useEvaluateFlag(path: string, defaultValue: number, confidence?: ConfidenceReact): FlagEvaluation<number>;
+
+// @public (undocumented)
+export function useEvaluateFlag(path: string, defaultValue: boolean, confidence?: ConfidenceReact): FlagEvaluation<boolean>;
+
+// @public (undocumented)
+export function useEvaluateFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): FlagEvaluation<T>;
+
+// Warning: (ae-missing-release-tag) "useFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-missing-release-tag) "useFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-missing-release-tag) "useFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
 // @public
-export function useFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): Value.Widen<T>;
+export function useFlag(path: string, defaultValue: string, confidence?: ConfidenceReact): string;
+
+// @public (undocumented)
+export function useFlag(path: string, defaultValue: number, confidence?: ConfidenceReact): number;
+
+// @public (undocumented)
+export function useFlag(path: string, defaultValue: boolean, confidence?: ConfidenceReact): boolean;
+
+// @public (undocumented)
+export function useFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): T;
 
 // @public
 export function useWithContext(context: Context, parent?: ConfidenceReact): ConfidenceReact;
diff --git a/api/sdk.api.md b/api/sdk.api.md
index 25280b59..205b3361 100644
--- a/api/sdk.api.md
+++ b/api/sdk.api.md
@@ -24,10 +24,22 @@ export class Confidence implements EventSender, Trackable, FlagResolver {
     readonly contextChanges: Subscribe<string[]>;
     static create({ clientSecret, region, timeout, environment, fetchImplementation, logger, }: ConfidenceOptions): Confidence;
     get environment(): string;
-    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
+    evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+    // (undocumented)
+    evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+    // (undocumented)
+    evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+    // (undocumented)
+    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
     get flagState(): State;
     getContext(): Context;
-    getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
+    getFlag(path: string, defaultValue: string): Promise<string>;
+    // (undocumented)
+    getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+    // (undocumented)
+    getFlag(path: string, defaultValue: number): Promise<number>;
+    // (undocumented)
+    getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
     // Warning: (ae-forgotten-export) The symbol "AccessiblePromise" needs to be exported by the entry point index.d.ts
     protected resolveFlags(): AccessiblePromise<void>;
     setContext(context: Context): boolean;
@@ -125,8 +137,14 @@ export type FlagEvaluation<T> = FlagEvaluation.Resolved<T> | FlagEvaluation.Stal
 
 // @public
 export interface FlagResolver extends Contextual<FlagResolver> {
-    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
-    getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
+    evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+    evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+    evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+    evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
+    getFlag(path: string, defaultValue: string): Promise<string>;
+    getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+    getFlag(path: string, defaultValue: number): Promise<number>;
+    getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
     subscribe(onStateChange?: StateObserver): () => void;
 }
 
@@ -178,7 +196,6 @@ export namespace Value {
         readonly [key: string]: Value;
     };
     export type TypeName = 'number' | 'string' | 'boolean' | 'Struct' | 'List' | 'undefined';
-    export type Widen<T extends Value> = T extends number ? number : T extends string ? string : T extends boolean ? boolean : T;
 }
 
 // @public
diff --git a/packages/react/src/index.tsx b/packages/react/src/index.tsx
index 80d4bf4c..17d92aef 100644
--- a/packages/react/src/index.tsx
+++ b/packages/react/src/index.tsx
@@ -119,12 +119,20 @@ export class ConfidenceReact implements EventSender, Trackable, FlagResolver {
     return new ConfidenceReact(this.delegate.withContext(context));
   }
   /** Evaluates a flag */
-  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>> {
+  evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+  evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+  evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
+  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T> {
     this.assertContext('evaluateFlag', 'useEvaluateFlag');
     return this.delegate.evaluateFlag(path, defaultValue);
   }
   /** Returns flag value for a given flag */
-  getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>> {
+  getFlag(path: string, defaultValue: string): Promise<string>;
+  getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+  getFlag(path: string, defaultValue: number): Promise<number>;
+  getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
+  getFlag<T extends Value>(path: string, defaultValue: T): Promise<T> {
     this.assertContext('getFlag', 'useFlag');
     return this.delegate.getFlag(path, defaultValue);
   }
@@ -142,12 +150,20 @@ export class ConfidenceReact implements EventSender, Trackable, FlagResolver {
     return useWithContext(context, this);
   }
   /** Hook to use EvaluateFlag functionality */
-  useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>> {
+  useEvaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+  useEvaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+  useEvaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+  useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
+  useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T> {
     this.assertContext('useEvaluateFlag', 'evaluateFlag');
     return useEvaluateFlag(path, defaultValue, this);
   }
   /** Hook to use getFlag functionality */
-  useFlag<T extends Value>(path: string, defaultValue: T): Value.Widen<T> {
+  useFlag(path: string, defaultValue: string): string;
+  useFlag(path: string, defaultValue: number): number;
+  useFlag(path: string, defaultValue: boolean): boolean;
+  useFlag<T extends Value>(path: string, defaultValue: T): T;
+  useFlag<T extends Value>(path: string, defaultValue: T): T {
     this.assertContext('useFlag', 'getFlag');
     return useFlag(path, defaultValue, this);
   }
@@ -233,12 +249,32 @@ export function useConfidenceContext(confidence = useConfidence()): Context {
 /**
  * Use EvaluateFlag
  * @public */
+export function useEvaluateFlag(
+  path: string,
+  defaultValue: string,
+  confidence?: ConfidenceReact,
+): FlagEvaluation<string>;
+export function useEvaluateFlag(
+  path: string,
+  defaultValue: number,
+  confidence?: ConfidenceReact,
+): FlagEvaluation<number>;
+export function useEvaluateFlag(
+  path: string,
+  defaultValue: boolean,
+  confidence?: ConfidenceReact,
+): FlagEvaluation<boolean>;
+export function useEvaluateFlag<T extends Value>(
+  path: string,
+  defaultValue: T,
+  confidence?: ConfidenceReact,
+): FlagEvaluation<T>;
 export function useEvaluateFlag<T extends Value>(
   path: string,
   defaultValue: T,
   // eslint-disable-next-line react-hooks/rules-of-hooks
   confidence = useConfidence(),
-): FlagEvaluation<Value.Widen<T>> {
+): FlagEvaluation<T> {
   const evaluation = confidence.delegate.evaluateFlag(path, defaultValue);
   const [, setState] = useState(() => confidence.contextState);
   useEffect(() => {
@@ -254,7 +290,11 @@ export function useEvaluateFlag<T extends Value>(
  * Use Flag
  * @public
  */
+export function useFlag(path: string, defaultValue: string, confidence?: ConfidenceReact): string;
+export function useFlag(path: string, defaultValue: number, confidence?: ConfidenceReact): number;
+export function useFlag(path: string, defaultValue: boolean, confidence?: ConfidenceReact): boolean;
+export function useFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): T;
 // eslint-disable-next-line react-hooks/rules-of-hooks
-export function useFlag<T extends Value>(path: string, defaultValue: T, confidence = useConfidence()): Value.Widen<T> {
+export function useFlag<T extends Value>(path: string, defaultValue: T, confidence = useConfidence()): T {
   return useEvaluateFlag(path, defaultValue, confidence).value;
 }
diff --git a/packages/sdk/src/Confidence.ts b/packages/sdk/src/Confidence.ts
index 48a190ac..c27fd8dd 100644
--- a/packages/sdk/src/Confidence.ts
+++ b/packages/sdk/src/Confidence.ts
@@ -276,7 +276,11 @@ export class Confidence implements EventSender, Trackable, FlagResolver {
   }
 
   /** Evaluates a flag */
-  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>> {
+  evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+  evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+  evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
+  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
+  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T> {
     let evaluation: FlagEvaluation<T>;
     // resolveFlags might update state synchronously
     if (!this.currentFlags && !this.pendingFlags) this.resolveFlags();
@@ -297,13 +301,17 @@ export class Confidence implements EventSender, Trackable, FlagResolver {
         ...evaluation,
         then,
       };
-      return staleEvaluation as FlagEvaluation<Value.Widen<T>>;
+      return staleEvaluation;
     }
-    return evaluation as FlagEvaluation<Value.Widen<T>>;
+    return evaluation;
   }
 
   /** Returns flag value for a given flag */
-  async getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>> {
+  getFlag(path: string, defaultValue: string): Promise<string>;
+  getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+  getFlag(path: string, defaultValue: number): Promise<number>;
+  getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
+  async getFlag<T extends Value>(path: string, defaultValue: any): Promise<T> {
     return (await this.evaluateFlag(path, defaultValue)).value;
   }
 
diff --git a/packages/sdk/src/Value.ts b/packages/sdk/src/Value.ts
index 6025568b..c05c8977 100644
--- a/packages/sdk/src/Value.ts
+++ b/packages/sdk/src/Value.ts
@@ -20,15 +20,6 @@ export namespace Value {
   /** Readonly List */
   export type List = ReadonlyArray<number> | ReadonlyArray<string> | ReadonlyArray<boolean>;
 
-  /** Sets Confidence used Values to be implementations of primitive types */
-  export type Widen<T extends Value> = T extends number
-    ? number
-    : T extends string
-    ? string
-    : T extends boolean
-    ? boolean
-    : T;
-
   /** Asserts a Value */
   export function assertValue(value: unknown): asserts value is Value {
     switch (typeof value) {
diff --git a/packages/sdk/src/flags.ts b/packages/sdk/src/flags.ts
index dbbef759..bc08849a 100644
--- a/packages/sdk/src/flags.ts
+++ b/packages/sdk/src/flags.ts
@@ -69,9 +69,21 @@ export interface FlagResolver extends Contextual<FlagResolver> {
   /** Subscribe to flag changes in Confidence */
   subscribe(onStateChange?: StateObserver): () => void;
 
+  /** Evaluates a string flag */
+  evaluateFlag(path: string, defaultValue: string): FlagEvaluation<string>;
+  /** Evaluates a boolean flag */
+  evaluateFlag(path: string, defaultValue: boolean): FlagEvaluation<boolean>;
+  /** Evaluates a numeric flag */
+  evaluateFlag(path: string, defaultValue: number): FlagEvaluation<number>;
   /** Evaluates a flag */
-  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
+  evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<T>;
 
-  /** Returns flag value for a given flag */
-  getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
+  /** Returns flag value for a string flag */
+  getFlag(path: string, defaultValue: string): Promise<string>;
+  /** Returns flag value for a boolean flag */
+  getFlag(path: string, defaultValue: boolean): Promise<boolean>;
+  /** Returns flag value for a numeric flag */
+  getFlag(path: string, defaultValue: number): Promise<number>;
+  /** Returns flag value for a flag */
+  getFlag<T extends Value>(path: string, defaultValue: T): Promise<T>;
 }