将条件类型映射回联合类型?

时间:2018-11-01 21:30:28

标签: javascript typescript generics conditional-types

我正在尝试执行以下操作(也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参数的条件类型来完成它。


编辑:基于以下Erik的回答,我也很好奇this one为什么行不通,以及是否有可能在不重新定义ResponseResult且不更改返回值的情况下使其工作的问题。功能。

@Erik,second example

2 个答案:

答案 0 :(得分:2)

您需要将类型封装为(假设)打字稿不能假定(计算)两个未引用的条件类型相同。

所以相反

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

  

有没有办法使其安全(无类型转换)?

不必要

答案 1 :(得分:1)

或者,为了获得类型推断的作用,您可以提供覆盖声明:

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()