【问题标题】:Map conditional types back to a union type?将条件类型映射回联合类型?
【发布时间】:2019-04-06 04:05:00
【问题描述】:

我正在尝试执行以下操作(也是 see it on TypeScript playground),但函数的返回类型出现错误,告诉我条件类型无法分配给联合:

type RequestType =
  | 'foo'
  | 'bar'
  | 'baz'

interface SomeRequest {
  id: string
  type: RequestType
  sessionId: string
  bucket: string
  params: Array<any>
}

type ResponseResult = string | number | boolean

async function sendWorkRequest<T extends RequestType>(
    type: T,
    ...params
  ): Promise<
    T extends 'foo'
      ? string
      : T extends 'bar'
        ? number
        : T extends 'baz' ? boolean : never
  > {
    await this.readyDeferred.promise

    const request: SomeRequest = {
      id: 'abc',
      bucket: 'bucket',
      type,
      sessionId: 'some session id',
      params: [1,'two',3],
    }
    const p = new Promise<ResponseResult>((/*...*/) => {/*...*/})

    this.requests[request.id] = p
    this.worker.postMessage(request)
    return p // <-------------------------------- ERROR
  }

基本上,我希望条件类型产生ResponseResult 类型之一。因此,根据传递给函数的 type 参数,它应该返回 ResponseResult 联合中的一种类型(作为 Promise)。

我怎样才能做到这一点,以便 type 参数的类型决定返回的 Promise 的类型?


这里是another way 不使用条件类型,但我想知道是否可以使用type arg 的条件类型来完成。


编辑:根据 Erik 在下面的回答,我也很好奇为什么 this one 不起作用,以及是否可以在不重新定义 ResponseResult 且不更改函数的返回类型的情况下使其工作。

@Erik,second example

【问题讨论】:

  • 一旦你声明了你的SomeRequest.RequestType,你永远无法在编译时知道实际的类型。为什么不将SomeRequest&lt;TType extends RequestType&gt;type: TType 作为属性?
  • @PatrickRoberts 没关系,这只是类型的问题。实际上它会得到解决。
  • @ErikPhilips 不完全确定您的意思。介意编辑我的游乐场示例吗?

标签: javascript typescript generics conditional-types


【解决方案1】:

您需要将 Type 封装为 (assumption) typescript 不能假设(计算)两个未引用的条件类型是相同的。

所以改为这样做

type ResponseResult<T> =
  T extends 'foo'
    ? string
    : T extends 'bar'
      ? number
      : T extends 'baz' ? boolean : never;

现在您可以将函数的签名更改为:

async function sendWorkRequest<T extends RequestType>(
  type: T,
  ...params
  ): Promise<ResponseResult<T>> {

并更新p:

const p = new Promise<ResponseResult<T>>(() => { });

TypeScript Playground Example

你知道为什么或者如何在不改变返回类型和不修改返回类型定义的情况下这样做吗?

不,因为 Conditional Type 不等于 Type

是否需要在调用函数的地方进行显式类型转换?

不,我可以使用 type 属性来模拟 promise 并查看它是否属于该类型:

TypeScript Playground Example

有没有办法让它安全(无类型转换)?

没必要

【讨论】:

  • 超级骗子太棒了!谢谢!在我附加到我的问题的示例游乐场中(基于您在此处的答案),返回值不会被推断为字符串。您知道为什么或如何在不更改返回类型和不修改返回类型定义的情况下这样做吗?那是否需要在调用函数的地方进行显式类型转换?有没有办法让它安全(没有类型转换)?
  • 我的意思是,例如 goo.gl/DKAg9B,看看我用as string 投射的位置。是否可以在不进行强制转换、修改 Promise&lt;ResponseResult&gt; 返回类型以及在该示例中不修改 ResponseResult 的定义的情况下做到这一点?
  • 是的,停止更改结果类型为: Promise&lt;ResponseResult&gt;,并使用正确的结果类型: Promise&lt;ResponseResultOptions&lt;T&gt;&gt;
  • 我明白了。我只是好奇是否可以将返回类型保留为Promise&lt;ResponseResult&gt;,所以我想你的意思是“不”。 :)
【解决方案2】:

或者,为了使类型推断正常工作,您可以提供覆盖声明:

type ResponseResult = string | number | boolean

async function sendWorkRequest(type: 'foo', ...params): Promise<string>
async function sendWorkRequest(type: 'bar', ...params): Promise<number>
async function sendWorkRequest(type: 'baz', ...params): Promise<boolean>
async function sendWorkRequest(type: RequestType, ...params): Promise<ResponseResult> {
  /* ... */
  const p = new Promise<ResponseResult>((/* ... */) => {/* ... */})
  /* ... */
}

// WORKS
async function test1() {
  const result = await sendWorkRequest('foo')
  result.split('')
}

test1()

【讨论】:

    猜你喜欢
    • 2019-01-12
    • 1970-01-01
    • 2021-01-26
    • 2019-03-21
    • 1970-01-01
    • 2022-01-13
    • 2019-12-11
    • 2021-01-31
    • 2021-06-04
    相关资源
    最近更新 更多