Search code examples
typescriptgenericsnarrowing

Narrow generic mapped argument


Here is code:

type T = 'a' | "b"
type M = {
  a: 1,
  b: 2
}

function a(a: 'a') {}
function m1(a: 1) {}

function f<TT extends T>(t: TT, p: M[TT]) {
  switch (t) {
    case "a": {
      a(t)
      m1(p) // Argument of type '1 | 2' is not assignable to parameter of type '1'
      break
    }
  }
}

Why p wasn't narrowed to 1? Are there a neat workaround without type casts?


Solution

  • TypeScript is unable to recognize that p corrolates with t as explained in microsoft/TypeScript#35873. You'll need another type guard to narrow p. The most straightforward syntax is to use switch (true) narrowing which was recently added in TypeScript 5.3.

    type T = "a" | "b";
    type M = {
      a: 1;
      b: 2;
    };
    
    declare function a(a: "a"): void;
    declare function m1(a: 1): void
    
    function f<TT extends T>(t: TT, p: M[TT]) {
      switch (true) {
        case t === "a" && p === 1:
          a(t);
          m1(p);
          break;
      }
    }
    

    TypeScript Playground