将元组类型换新

时间:2019-10-05 20:46:55

标签: arrays typescript arguments tuples

我可以编写以下代码和it works

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] {

doesn't compile出错

  

休息元素类型必须是数组类型。

我该如何解决?

1 个答案:

答案 0 :(得分:1)

语法[H, ...T]proposed,但不受支持。对tuples in rest/spread positions的支持使我们可以代表同样的概念:

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>>,其中Tmap参数属性中的数组类型。还要注意,在map中,我使属性类型为T | [],而不仅仅是T。这是trick,它向编译器暗示您希望map的类型为inferred as a tuple if possible。否则,当您使用["a", 1]之类的数组文字时,通常会推断为Array<string | number>而不是[string, number]。由于您的cases()函数在很大程度上依赖于添加到元组,因此,如果编译器为您推断出元组,则将很有帮助。这可能是您无法进行这项工作的原因吗?即使没有这个,您也可以使用const assertions或其他注释来使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
}

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

Link to code

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
}