Search code examples
typescriptuniontype-inference

How to narrow type of parameter in function?


I have type narrow problem. how to narrow type of parameter v to successfully pass it ot funcAorB?

// type.d.ts
export type A = "A1" | "A2";
export type B = "B1" | "B2";
export type U = A | B;

export function funcAorB(v: A) : A;
export function funcAorB(v: B) : B;

// test.ts
function wrappFunAorB<T extends U>(v:T) {
  type V<K> = K extends A ? A : B; 
  funcAorB(v as V<T>) // error
}


I expected V<T> would narrow type of T but is didn't


Solution

  • Since you've defined funcAorB using overloads, TypeScript needs to know which specific overload to call (even though either way, it ends up calling the same implementation function). That is, it has to know whether the runtime v will be an A or a B. A compile-time check won't work.

    You have at least two choices:

    1. Implement a type predicate to narrow v, for instance:

      function isA(value: any): value is A {
          return value === "A1" || value === "A2"; // Just an example, you probably want something more robust, where the check and the type have the same single source
      }
      

      ...and then use logic so TypeScript sees a narrowed type, for instance:

      function wrappFunAorB<T extends U>(v :T) {
          if (isA(v)) {
              funcAorB(v);
          } else {
              funcAorB(v);
          }
      }
      

      Playground link

    2. Implement the function accepting a union type instead, rather than using overloads. It can either directly accept the union:

      function funcAorB(v: A | B): A | B {
          // ...
      }
      

      Playground link

      Or it can use a generic type parameter:

      function funcAorB<T extends A | B>(v: T): T {
          // ...
      }
      

      Playground link