【问题标题】:How do I map return types into a tuple?如何将返回类型映射到元组?
【发布时间】:2020-06-27 23:15:24
【问题描述】:

我有一个实例数组,其中执行返回一个类型,例如 2 个类,其实例在数组中:

class ClassBase<TReturn>
{
   execute (): TReturn
}

class ReturnsString extends ClassBase<string>
{
   execute () : string
}

class ReturnsNumber extends ClassBase<number>
{
   execute () : number
}

const items:[ReturnsString,ReturnsNumber] = [new ReturnsString(), new ReturnsNumber()];

在一个方法中,我将为元组中的每个项目循环调用执行。

如何声明一个方法

  • 接受项目 - 项目可以是任意长度并且可以包含各种类型,例如。可以是 [ReturnString] 或 [ReturnString,ReturnString,ReturnString,ReturnNumber,....]

  • 返回返回类型元组的类型[string,string,string,number]

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您可以使用映射类型来映射元组并提取每个元组项的返回类型:

    type ReturnsOfClassBase<T extends Record<number, ClassBase<any>>> = {
      -readonly [P in keyof T] : T[P] extends ClassBase<infer R> ? R: never
    }
    function getReturns<T extends readonly ClassBase<any>[]>(p: T): ReturnsOfClassBase<T> {
      const result = []
      for(let r of p) {
        result.push(r.execute());
      }
    
      return result as any
    }
    
    let r = getReturns(items)
    
    

    Playground Link

    【讨论】:

    • 我试过这个解决方案,但它仍然返回一个联合元组。问题想要的是返回一个与输入元组的长度和顺序匹配的元组类型。
    • @CMCDragonkai 如果您检查操场链接,您会看到r[string, number],因此它适用于给定的示例。如果有什么不适合你问另一个问题,我可以看看
    • 我做了检查,并运行了我自己的示例。但我注意到你的as constitems。没有它,它会回到联合数组。
    • as const 在做什么?而且你的回答中没有提到。
    • @CMCDragonkai 这个答案假定参数(在本例中为 items )已经是一个元组。如果您希望在使用getReturns( [new ReturnsString(), new ReturnsNumber()]) 调用函数时将T 推断为元组,则可以使用T extends (readonly [ClassBase&lt;any&gt;] | readonly ClassBase&lt;any&gt;[]) 作为约束。
    【解决方案2】:

    我最近实现了一个类似的类型:

    type TransformTuple<Tuple extends (() => any)[], Returns extends any[] = []> =
      Tuple extends [() => any, ...infer More] ?
        More extends (() => any)[] ?
          TransformTuple<More, [...Returns, ReturnType<Tuple[0]>]>
          : never
        : Returns
    
    type Test = TransformTuple<[() => 1, () => "2"]>
    /*
    type Test = [1, "2"]
    */
    
    //NOTE Only tuples are supported
    
    //If use normal array like this:
    type Test2 = TransformTuple<Array<() => "never">>
    /*
    //You only get an empty array
    type Test2 = []
    */
    

    【讨论】:

      【解决方案3】:

      好问题!它让我想了很多,并使用了许多高级 TS 打字功能,但我想这就是你要找的:

      abstract class ClassBase<TReturn = any>
      {
         abstract execute (): TReturn
      }
      
      type FlattenIfArray<T> = T extends (infer R)[] ? R : T
      type ExtendsOf<T> = T extends ClassBase<infer R> ? R : T
      type ExecuteArrayReturn<T> = ExtendsOf<FlattenIfArray<T>>
      
      function example<T extends ClassBase<ExecuteArrayReturn<T>>[]>(items: T): ExecuteArrayReturn<T> {
          return items.map(item => item.execute()) as any
      }
      
      class ReturnsString extends ClassBase<string>
      {
         execute () : string {return 'a'}
      }
      
      class ReturnsNumber extends ClassBase<number>
      {
         execute () : number {return 1}
      }
      
      const items:[ReturnsString,ReturnsNumber] = [new ReturnsString(), new ReturnsNumber()];
      
      const result = example(items);
      

      我需要创建一些类型来从ClassBase 继承人的实例中提取execute 函数返回值。 FlattenIfArray 获取数组项的类型,ExtendsOf 获取已设置为 T 的泛型类型的类型,ExecuteArrayReturn 将两者加入。

      当你实现真正的函数时,不要忘记函数return 上的as any。否则,TS 会认为你要返回一个由提供的 items 类型 union 组成的数组,例如:(string | number)[]

      【讨论】:

      • 但是这个解决方案返回一个联合类型的数组。该请求明确地针对一个元组。而且可以做到,看我的回答:)
      猜你喜欢
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      • 2019-01-11
      • 1970-01-01
      • 2017-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多