TypeScript中用于元组的泛型包装

jus*_*.me 7 generics types tuples typescript

我需要向函数添加类型声明,以将元组的元素映射[Foo<A>, Foo<B>, ...]到函数() => [A, B, ...]。如何在TypeScript中实现这一目标?

该示例在结构上与应用程序的相关部分相似:

interface SomethingWithAValue<T> { value: T; }

function collect(array) {
  return () => array.map(a => a.value);
}
Run Code Online (Sandbox Code Playgroud)

这将返回与每个对象关联的值的元组。类型声明collect看起来像什么?

伪代码:

function collect<[SomethingWithAValue<T>...](array: [SomethingWithAValue<T>...]): () => [T...];
Run Code Online (Sandbox Code Playgroud)

根据jonrsharpe的建议进行更新:

interface SomethingWithAValue<T> { value: T; }

function collect<T>(array: SomethingWithAValue<T>[]): () => T[] {
  return () => array.map(a => a.value);
}

type myTupleType = [string, number];

let somethings: [SomethingWithAValue<string>, SomethingWithAValue<number>];
somethings = [{ value: 'foo' }, { value: 5 }];

let fn: () => myTupleType = collect(somethings);
Run Code Online (Sandbox Code Playgroud)

这不起作用:

Argument of type '[SomethingWithAValue<string>, SomethingWithAValue<number>]' is not assignable to parameter of type 'SomethingWithAValue<string>[]'.
  Types of property 'pop' are incompatible.
    Type '() => SomethingWithAValue<string> | SomethingWithAValue<number>' is not assignable to type '() => SomethingWithAValue<string>'.
      Type 'SomethingWithAValue<string> | SomethingWithAValue<number>' is not assignable to type 'SomethingWithAValue<string>'.
        Type 'SomethingWithAValue<number>' is not assignable to type 'SomethingWithAValue<string>'.
          Type 'number' is not assignable to type 'string'.
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 8

更新:从TS3.1开始,以下答案已过时。我相信您现在可以使用映射的元组类型条件类型推断来获取所需的行为:

type ExtractValue<T extends ReadonlyArray<SomethingWithAValue<any>>> =
  { [K in keyof T]: T[K] extends SomethingWithAValue<infer V> ? V : never };

function collect<T extends ReadonlyArray<SomethingWithAValue<any>>>(
  array: T
): () => ExtractValue<T> {
  return () => array.map(a => a.value) as any;
}
Run Code Online (Sandbox Code Playgroud)

让我们使用它...首先,让我们轻松地通过一个带有辅助函数的元组类型,该函数tuple()接受各种参量并输出一个元组(自TS3.0起已成为可能)

type Narrowable = string | number | boolean | undefined | null | void | {};
const tuple = <T extends Narrowable[]>(...t: T) => t;
Run Code Online (Sandbox Code Playgroud)

让我们看看它是否有效:

const result = collect(tuple({ value: 10 }, { value: "hey" }, { value: true }));
// const result: () => [number, string, boolean]
Run Code Online (Sandbox Code Playgroud)

看起来不错!


旧答案:

TypeScript 中没有可变参数类型,因此无法键入通用元组(语法不[T...]存在)。

解决方法是,您可以为任何长度的元组提供函数重载,直到合理的最大值:

function collect<A, B, C, D, E>(array: [SomethingWithAValue<A>, SomethingWithAValue<B>, SomethingWithAValue<C>, SomethingWithAValue<D>, SomethingWithAValue<E>]): () => [A, B, C, D, E];
function collect<A, B, C, D>(array: [SomethingWithAValue<A>, SomethingWithAValue<B>, SomethingWithAValue<C>, SomethingWithAValue<D>]): () => [A, B, C, D];
function collect<A, B, C>(array: [SomethingWithAValue<A>, SomethingWithAValue<B>, SomethingWithAValue<C>]): () => [A, B, C];
function collect<A, B>(array: [SomethingWithAValue<A>, SomethingWithAValue<B>]): () => [A, B];
function collect<A>(array: [SomethingWithAValue<A>]): () => [A];
function collect<T>(array: SomethingWithAValue<T>[]): () => T[] {
  // implementation
}
Run Code Online (Sandbox Code Playgroud)

这适用于长度最大为5的元组,并且您可以在顶部添加其他重载以适应实际需要。它很冗长和丑陋,但应该可以使用。

希望能有所帮助;祝好运!