复杂类型层次结构将叶子映射到“未知”类型

发布于 2025-01-17 01:27:32 字数 4471 浏览 2 评论 0原文

我正在尝试为 API 外观构建一个灵活的通用基类。这些外观将定义一组返回特定​​类型的 HTTP 请求。这些定义用于构建自定义的请求注册表并在每个外观的基础上进行跟踪。我希望这些请求能够提供所有类型信息,但是我认为我在映射类型方面遇到了问题,因为在其核心,我使用的是`FacadeResultTypes = Record;

<一href="https://www.typescriptlang.org/play?#code/PQ19gUCAEMCoE8AOBTAzjdqAuBXZOEaCU8SEmAYQHsA7AN1ToEtmBj VGGTAJwEMcNXpBwouANX4AbPKgDyAMwaA88AHwwAvAgDaAa1SIaihaAF0A3BVgARDO14sARhhj86MABLx4ABRi9UAEc5dBwAckw6fgBbL JCAE38MPGkcG​​DE0GABaGBZo5GlUWLocVETHRFd3Fjp4lgYWeLwZGEV+dn54rmxeBs4AOkoAORpS9IALQQmuAAM8Oj06GgB3Ohn08Rh x1ADczAD0FJwagHNc9xxtvcPUQYyuADF2ztQAJWTUpDRMbXf2YXiylCDjoJwanANDB5osVnQ1JZKLZFDUXPwsKdChtMsYksEMGloi8YBU qjAAII+ACSrWeXX6CAE7D0kTw0WcvG4JhqimkLBO4zSAVxoXQEJ2vGEIqqiWkgjxMAYMjkMAAFArZFx7nsYPYWMcOtIAJSiTZPDpdd 5CnCqGCoAAepVqmCk6qUylNL3eh0+4nQag02gA3pA4DUHjy+TgAFwwOgstmWOAy0qhaPwAA+8y6SLoZQTNt44t40eBpwztVQ2dzkAAv lZgGSYHV2Md6PxeJUhDBpDQaHpIYR3eagiEcJhiVFYiqJ1x-rFMIpxdFpjBB28Pjgvi4DIhJWqlSxMDU6g0mi1BSP0EbNauLSPrCxm yxW+3lJ6jpvMHaHfFMDf1x+1GDLQYD+AFlG3bE329b4IT-S1lGdORXSgjcfT9OE63pdomWXJM5XPOVO1nApcFQCF+EwZZUGkaRXEwf4 Sn4GpMFROC7wfFsonbDlXBonEL1aYRlzaM1UGNTI2Lxd4TgPHAXxQj8bXtZgfxXGk1y9VDvn9GAg0TWUUzU0TbzxBDFQUFQFLQjQ00 hctK3iPMZGkaNJNCe9H2fRBX3-NDLFrCgSDIYLiFgGAAFFbRiEjIXQfgTi4GBQpCkKKBqUpeBERhIuiwpPWQehsE3XSgOTKMsDk048z KgAmaNY1ZHZ-LSkodiyiKovyQo-00xSvxU391Ks74SrgO1crXS1oxyrqNIKugivEPNYFGbZ2QI0IYxiVBgAOebsExLhkCY3hMBOGhz gmA8u1kmsKAYzbxtm6TZPbVyhuHKTUBk4FvJmkievfNDgL0rsDPKzMK2ReIwSA5zo1BsbOpIkzDIDOAMdgZQshyABZfhkEwTsRI9T7Q MUKEllWQCMYx0Nw35aMAAZYdp-SyujSGHIhJL62xvGCaJi65gWehVhmIDabFYROfs6HWYx2tadgeRLh2fjCJ9RsoZzRIamXf7uo+3q tfStr2i4ZYaBSRJ0HGFZ+y2HYxLgWtq3hWAfFeeQACEABlwtxyNKAAdXGFgMSelHvteyoEiUiaXt+-pnJgAlCE7NWWHZf4C1QZtpEqA 4aGkJhEnuSUo7ymPk+c-oq8mkc08F9ILpJod4MpmENHj3V6PD6REn6PDNpkdALvToW7OhVZBlgKkrZtm6DAmGgDpFme1kOqf09b6Zs +1YQ9voOpQSM0mTe+Of6wAZTQdgWCRfVC4hBe3DSBuk7kxAU5o+vkerpaYe4M96707IbOahVUCbkGJ-Gu39f7SH-onMmOBgFLUSEAA” rel="nofollow noreferrer">这是一个 TS 游乐场,我在其中重新创建了类型生态系统。上部是层次结构的设置,下部是重现我的问题的有限示例。

///////////////////
//  Types setup  //
///////////////////

// Convenience operator
type ValueOf<T> = T[keyof T];

// Describes an HTTP request's name and result type - implemented by an individual facade service.
// Note that the `unknown` type here is resulting in the issue.
type FacadeResultTypes = Record<string, unknown>;

// Defines a single type of request made by an API facade. Tracks number of inflight requests, errors, and latest value (value type is critical)
type FacadeRequest<T extends ValueOf<FacadeResultTypes>> = {
  inFlight: number;
  latest: T|undefined;
  error: string|undefined;
}

// A dictionary to look up FacadeRequests by name (name comes from the FacadeResultTypes keys, value is individual requests)
type FacadeRequestDictionary<ResultTypes extends FacadeResultTypes>
  = Record<keyof ResultTypes, FacadeRequest<ValueOf<ResultTypes>>>;

// Tracks the latest request to complete, as well as contains a FacadeRequestDictionary of all requests for the facade
type FacadeRequestRegistry<ResultTypes extends FacadeResultTypes> = {
  latest: FacadeRequest<ValueOf<ResultTypes>> | undefined;
  all: FacadeRequestDictionary<ResultTypes>;
}



/////////////////////
//  Example usage  //
/////////////////////

interface ExampleResponseType {
  test: string;
  test2: number;
}

interface ExampleFacadeResultTypes extends FacadeResultTypes {
  exampleRequest: ExampleResponseType;
  // other request name/response type pairs go in this list
}

const exampleRegistry: FacadeRequestRegistry<ExampleFacadeResultTypes> = {
  latest: undefined,
  all: {
    exampleRequest: {     // <-- Maps to facadeRequest<unknown>
      inFlight: 0,
      latest: undefined,  // <-- Maps to `unkonwn`
      error: undefined,
    }
    // Other request types defined in the ExampleFacadeResultTypes interface would show up here
  }
};

// PROBLEM:
// While exampleRegistry and exampleRegistry.all map to their correctly resolved types, exampleRegistry.all.exampleRequest maps to facadeRequest<unknown> and its child .latest also maps to unknown.
// I would like those `unknown` types to map to their corresponding FacadeResultTypes.

// Specifically, I want exampleRegistry.all.exampleRequest.latest to map to ExampleResponseType.
exampleRegistry.all.exampleRequest.latest;

正如您所看到的,我不希望 unknown (这需要实现者进行类型转换)出现在对层次结构中最低级别元素的最终引用中。

有人看到我在这里做错了什么吗?感谢您的任何帮助。

I'm trying to build a flexible generic base class for API facades. These facades would define a set of HTTP requests that return specific types. These definitions are used to build a registry of requests that is custom and tracked on a per-facade basis. I would like these requests to have all their type information available, however I think I'm having issues around mapped types because at its core I'm using a `FacadeResultTypes = Record<string, unknown>;

Here is a TS playground where I have recreated the type ecosystem. Upper portion is the setup of the hierarchy, and the lower portion is a limited example that reproduces my issue.

///////////////////
//  Types setup  //
///////////////////

// Convenience operator
type ValueOf<T> = T[keyof T];

// Describes an HTTP request's name and result type - implemented by an individual facade service.
// Note that the `unknown` type here is resulting in the issue.
type FacadeResultTypes = Record<string, unknown>;

// Defines a single type of request made by an API facade. Tracks number of inflight requests, errors, and latest value (value type is critical)
type FacadeRequest<T extends ValueOf<FacadeResultTypes>> = {
  inFlight: number;
  latest: T|undefined;
  error: string|undefined;
}

// A dictionary to look up FacadeRequests by name (name comes from the FacadeResultTypes keys, value is individual requests)
type FacadeRequestDictionary<ResultTypes extends FacadeResultTypes>
  = Record<keyof ResultTypes, FacadeRequest<ValueOf<ResultTypes>>>;

// Tracks the latest request to complete, as well as contains a FacadeRequestDictionary of all requests for the facade
type FacadeRequestRegistry<ResultTypes extends FacadeResultTypes> = {
  latest: FacadeRequest<ValueOf<ResultTypes>> | undefined;
  all: FacadeRequestDictionary<ResultTypes>;
}



/////////////////////
//  Example usage  //
/////////////////////

interface ExampleResponseType {
  test: string;
  test2: number;
}

interface ExampleFacadeResultTypes extends FacadeResultTypes {
  exampleRequest: ExampleResponseType;
  // other request name/response type pairs go in this list
}

const exampleRegistry: FacadeRequestRegistry<ExampleFacadeResultTypes> = {
  latest: undefined,
  all: {
    exampleRequest: {     // <-- Maps to facadeRequest<unknown>
      inFlight: 0,
      latest: undefined,  // <-- Maps to `unkonwn`
      error: undefined,
    }
    // Other request types defined in the ExampleFacadeResultTypes interface would show up here
  }
};

// PROBLEM:
// While exampleRegistry and exampleRegistry.all map to their correctly resolved types, exampleRegistry.all.exampleRequest maps to facadeRequest<unknown> and its child .latest also maps to unknown.
// I would like those `unknown` types to map to their corresponding FacadeResultTypes.

// Specifically, I want exampleRegistry.all.exampleRequest.latest to map to ExampleResponseType.
exampleRegistry.all.exampleRequest.latest;

As you can see, I would like to not have unknown (which then requires type casting by the implementer) to show up in the final references to the lowest-level elements in the hierarchy.

Does anyone see what I'm doing wrong here? Thanks for any help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷了相思 2025-01-24 01:27:32
// Convenience operator
type ValueOf<T> = T[keyof T];

我不确定为什么你有这个 ValueOf 类型。那就行了:

Type A = { a: 1, b: 2 }
Type B = ValueOf<A> // 1 | 2

我不明白这对你的问题有什么帮助。

如果你完全删除它,我认为一切都会按你的预期进行。

exampleRegistry.all.exampleRequest.latest; //  ExampleFacadeResultTypes | undefined

参见游乐场

// Convenience operator
type ValueOf<T> = T[keyof T];

I'm not sure why you have this ValueOf type. That will just do:

Type A = { a: 1, b: 2 }
Type B = ValueOf<A> // 1 | 2

I don't see how this is helpful to your problem.

And if you remove that completely, I think everything works as you expect.

exampleRegistry.all.exampleRequest.latest; //  ExampleFacadeResultTypes | undefined

See Playground

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文