根据字符串参数从预先未知的联合中提取

Extract from beforehand unknown union based on string param

陷入了我无法解决的问题。

我想在我的 TypeScript 包中有如下功能:

function example<U>(key: keyof U) {
  return function<T extends U[typeof key]>(
    value: T, 
    cb: (obj: unknown /* not sure what this should be typed as */) => void
  ) {
    // ...
  }
}

并将预先未知的 Union 传入其中:

type Union =
  | {
      color: "red";
      status: "error"
    }
  | {
      color: "green";
      status: "success"
    }
  | {
      color: "blue";
      status: "info"
    }

我需要如何输入它才能达到所需的状态?

example<Union>("color")("red", (obj) => {
  // obj.color === "red"
  // obj.status === "error"
});

这可能吗?

谢谢。

尽管您必须使用包装器 class 或另一个包装器函数,但您可以这样做,因为 Union 的泛型必须手动传入,我们需要另一个泛型以便以后使用在 Union 的键上。如果我们在原始函数中包含这两个泛型,那么您的代码将会出错,因为如果您要包含其中任何一个,则必须显式包含所有泛型。这是我的解决方案:

function matcher<T>() {
  return function key<K extends keyof T>(key: K) {
    return function value<V extends T[K]>(value: V, cb: (data: T & { [_ in K]: V }) => any) {
      throw new Error("unimplemented");
    };
  }
}

type Union =
  | {
      color: "red";
      status: "error"
    }
  | {
      color: "green";
      status: "success"
    }
  | {
      color: "blue";
      status: "info"
    };


matcher<Union>()("color")("red", (obj) => {
  type Obj = typeof obj;
  //   ^? { color: "red"; status: "error"; }

  // obj.color === "red"
  // obj.status === "error"
});

我使用 T & { [_ in K]: V } 作为 cb 参数类型的原因是因为它会在联合中获得包含具有给定值的键的分支。

TypeScript Playground Link