我可以将开放式元组转换为封闭式元组吗?

时间:2019-09-18 17:29:31

标签: typescript

我正在尝试使用以下代码:

interface AstNode<T> {
  value: T;
}

const n1: AstNode<number> = { value: 1 };
const n2: AstNode<string> = { value: "asdf" };

// https://github.com/microsoft/TypeScript/pull/26063 says that if T is a tuple
// type, the mapped type will map each specific type in the tuple.
type Values<T> = { [P in keyof T]: T[P] extends AstNode<infer V> ? V : never };

function node<
  T,
  // https://github.com/microsoft/TypeScript/pull/24897 allows open ended tuple types, and
  // I want D to be open-ended here.
  D extends [...AstNode<any>[]],
  // But then here I want to convert it back to fixed-length to get [string, number] instead
  // of [string | number, string | number]
  A extends Values<D>
>(deps: D, fn: (...args: A) => T): AstNode<T> {
  const values = deps.map(d => d.value) as A;
  return { value: fn(...values) };
}

const n3: AstNode<string> = node([n1, n2], (v1, v2) => {
  const v1n: number = v1; // v1 is string | number
  const v2s: string = v2; // v2 is string | number
  return v1n + v2s;
});

我认为这实际上只是Promise.all问题的一个概括,其中Promise.all要用一个泛型类型的单个开放长度元组声明,并且其返回值将被映射键入原始/推断元组的定长版本。

在TypeScript中是否还可以进行类似的处理?跟踪此功能是否存在特定问题?

1 个答案:

答案 0 :(得分:1)

在这里,我真的不认为您的问题与开放式元组和固定长度有关。对于真正需要此功能的人,有open issue暗示了更多操作元组类型的能力,而我mused则能够指定给定长度和开放式的严格元组会很好给定前缀长度的元组。尽管那里似乎没有太多的事情发生。目前,该语言本身不支持此功能。您可能会自己伪造一些东西,如果需要的话,它至少适用于最大合理长度的元组。


可能的主要问题是TypeScript不会自动将诸如[n1, n2]之类的数组文字推导为诸如[AstNode<number>, AstNode<string>]之类的元组类型;相反,它将倾向于推断诸如Array<AstNode<number> | AstNode<string>>之类的数组类型,它会丢失顺序和长度信息,并导致number | string

这是我输入node()的方式:

function node<T, A extends any[]>(
  deps: { [K in keyof A]: AstNode<A[K]> } | [never],
  fn: (...args: A) => T
): AstNode<T> {
  const values = (deps as Array<AstNode<any>>).map(d => d.value) as A;
  return { value: fn(...values) };
}

请注意,我正在从您的数据库进行反向元组映射,在计算给定D的情况下A,而不是给定A的{​​{1}}。还要注意,没有真正的理由要为它们设置两个类型参数。您不需要额外的自由度,因为一旦有了D,您就知道A,因此我将其删除。

主要问题的解决方法来自D类型结尾处的| [never]。就允许使用deps的值而言,它实际上并没有做什么工作(因为我希望在我们的代码中不存在带有deps元素的单元组),但是它确实{{3 }}。是的,它并不漂亮;最好有一些give the compiler a hint that you want it to be inferred as a tuple type,但没有(至少从TS3.6开始)。

还要注意,never的新类型有点奇怪,因此在调用deps方法时我将其更改为(deps as Array<AstNode<any>>)

好吧,让我们看看它是如何工作的:

map()

看起来不错。希望能有所帮助;祝你好运!

less hacky way to ask for tuples