首页 > 解决方案 > 将元组类型转换为新类型

问题描述

我可以编写以下代码并且它可以工作

function cases<K extends string, V, U, T>(map: { [key in K]: [V, U, T] }): [K, V, U, T][];
function cases<K extends string, V, U>(map: { [key in K]: [V, U] }): [K, V, U][];
function cases<K extends string, V>(map: { [key in K]: V }): [K, V][];
function cases<K extends string, V>(map: { [key in K]: V }) {
  return Object.keys(map).map(key => ([key] as any[]).concat(map[key as K]) as any);
}

for (const [key, arr, res] of cases({
    "abc": [[1, 2, "qqq"], 'qwe'],
    "def": [[4, 5, "asd"], 'www'],
})) {
    // const key: "abc" | "def"
    // const arr: (string | number)[]
    // const res: string
}

但我不想写这一堆重载:

function cases<K extends string, V, U, T>(map: { [key in K]: [V, U, T] }): [K, V, U, T][];
function cases<K extends string, V, U>(map: { [key in K]: [V, U] }): [K, V, U][];
function cases<K extends string, V>(map: { [key in K]: V }): [K, V][];

并想指定一些元组类型:

function cases<K extends string, V extends any[]>(map: { [key in K]: V }): [K, ...V] {

但它不会编译错误

剩余元素类型必须是数组类型。

我该如何解决?

标签: arraystypescriptargumentstuples

解决方案


该语法[H, ...T]提出但不受支持。对rest/spread位置的元组的支持使我们可以像这样表示相同的概念:

type Cons<H, T extends readonly any[]> =
  ((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never;

观察:

type ConsTest = Cons<1, [2, 3, 4]> // [1, 2, 3, 4]

所以你的cases()函数可以这样输入:

function cases<K extends string, T extends any[]>(
  map: { [key in K]: T | [] }
): Array<Cons<K, T>> {
  return Object.keys(map).map(
    key => ([key] as any[]).concat(map[key as K]) as any
  );
}

在这里,我们返回参数属性中的数组类型在Array<Cons<K, T>>哪里。另请注意,在我创建了属性类型而不是. 这是一个提示编译器的技巧,如果可能的话,您希望将 的类型推断为元组。否则,当您使用类似 的数组文字时,这往往会被推断为而不是。由于您的函数在很大程度上依赖于添加到元组,因此编译器为您推断元组会很有帮助。这可能是您无法完成这项工作的原因吗?但是,即使没有这个,您也可以使用断言TmapmapT | []Tmap["a", 1]Array<string | number>[string, number]cases()const或其他注释以使您的map属性成为元组。

好的,让我们确保它按您的意愿工作:

for (const [key, arr, res] of cases({
  "abc": [[1, 2, "qqq"], 'qwe'],
  "def": [[4, 5, "asd"], 'www'],
})) {
  key; // "abc" | "def"
  arr; // (string | number) []
  res; // string
}

是的,看起来不错。好的,希望有帮助;祝你好运!

链接到代码

for (const [key, arr, res] of cases({
  "abc": [[1, 2, "qqq"], 'qwe'],
  "def": [[4, 5, "asd"], 'www'],
})) {
  key; // "abc" | "def"
  arr; // (string | number) []
  res; // string
}

推荐阅读