简体   繁体   English

TypeScript中用于元组的通用类型包装

[英]Generic type wrapping in TypeScript for tuples

I need to add a type declaration to a function which maps elements of a tuple [Foo<A>, Foo<B>, ...] to a function () => [A, B, ...] . 我需要向函数添加一个类型声明,该函数将元组[Foo<A>, Foo<B>, ...]元素映射到函数() => [A, B, ...] How can I achieve this in TypeScript? 我怎样才能在TypeScript中实现这一目标?

This example is structurally similar to the relevant part of the application: 此示例在结构上与应用程序的相关部分类似:

interface SomethingWithAValue<T> { value: T; }

function collect(array) {
  return () => array.map(a => a.value);
}

This returns a tuple of the values associated with each object. 这将返回与每个对象关联的值的元组。 What would the type declaration for collect look like? collect的类型声明会是什么样的?

Pseudocode: 伪代码:

function collect<[SomethingWithAValue<T>...](array: [SomethingWithAValue<T>...]): () => [T...];

Update in response to jonrsharpe's suggestion: 更新以回应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);

This does not work: 这不起作用:

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'.

UPDATE: The answer below is obsolete as of TS3.1. 更新:从TS3.1开始,下面的答案已经过时。 I believe you can now use mapped tuple types and conditional type inference to get the behavior you want: 我相信你现在可以使用映射的元组类型条件类型推断来获得你想要的行为:

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;
}

And let's use it... First let's make it easy to get a tuple type with a helper function tuple() which takes in a variadic number of arguments and outputs a tuple (this was made possible as of TS3.0) 让我们使用它......首先让我们很容易得到一个带有辅助函数tuple()的元组类型,它接受一个可变数量的参数并输出一个元组(这是从TS3.0开始实现的)

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

And let's see if it works: 让我们看看它是否有效:

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

Looks good! 看起来不错!


OLD ANSWER: 老答案:

There are no variadic kinds in TypeScript, so there's no way to type a generic tuple (nothing like the syntax [T...] exists). TypeScript中没有可变参数类型 ,因此无法输入通用元组(没有类似语法[T...]存在)。

As a workaround, you can provide function overloads for tuples of any length up to some reasonable maximum: 作为一种变通方法,您可以为任何长度的元组提供函数重载,直到达到一些合理的最大值:

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
}

That should work for tuples up to length 5, and you can add other overloads at the top to get up to whatever you need in practice. 这应该适用于长度为5的元组,并且您可以在顶部添加其他重载以实现您在实践中所需的任何内容。 It's verbose and ugly but it should work. 这是冗长而丑陋的,但应该有效。

Hope that helps; 希望有所帮助; good luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM