Смена типа кортежа на новый - PullRequest
0 голосов
/ 05 октября 2019

Я могу написать следующий код и он работает :

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

, но он не компилируется с ошибкой

Тип элемента rest должен быть типом массива.

Как я могу это исправить?

1 Ответ

1 голос
/ 07 октября 2019

Синтаксис [H, ...T] был , предложен , но не поддерживается. Поддержка кортежей в позициях покоя / спреда позволяет нам вместо этого представлять такую ​​же концепцию:

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

Observe:

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>>, где T - это тип массива в свойствах аргумента map. Также обратите внимание, что в map я сделал тип свойства T | [] вместо просто T. Это трюк , который намекает компилятору, что вы хотите, чтобы тип map был выведен как кортеж, если это возможно . В противном случае, когда вы используете литерал массива, такой как ["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
}
...