(TypeScript) аргумент получает свой тип от объединения возвращаемых типов его родного брата (все из которых являются fns) - PullRequest
1 голос
/ 04 ноября 2019

Я пытаюсь создать универсальную функцию, которая принимает два параметра, первый из которых представляет собой массив функций. Тип второго параметра должен быть объединением типов возврата элементов первого параметра (все из которых являются функциями).

Вот что я пробовал:

type Fn<R> = () => R;
const a: Fn<true> = () => true;
const b: Fn<"somen"> = () => "somen";

interface C<ABUnion, ABReturnUnion> {
  fns: ABUnion[];
  returns: ABReturnUnion;
}

function getX<R>(fns: Array<Fn<R>>, returns: R) {
  type FnsUnion = typeof fns[number];
  const c: C<FnsUnion, R> = {
    fns,
    returns,
  };
  return c;
}

getX([b, a], "true");

Языкслужба подчеркивает b в вызове getX и отображает следующую ошибку:

  Type '"somen"' is not assignable to type 'true'.

Кто-нибудь знает решение? Спасибо!

1 Ответ

2 голосов
/ 04 ноября 2019

Когда компилятор видит литерал гетерогенного массива, он использует некоторую эвристику, чтобы определить, синтезировать ли объединение для типа элемента или выдать ошибку в первом элементе, который не соответствует предполагаемому типу. Это компромисс, потому что иногда вы хотите такую ​​ошибку. Например, следующая ошибка обычно считается желательной, как упоминалось в связанном вопросе :

function same<T>(x: T, y: T) { };
same(0, ""); // error!
// ---> ~~
// argument of type "" is not assignable to number

Эта функция может быть превращена в пример массива, подобный этому:

function sameTuple<T>(x: [T, T]) { };
sameTuple([0, ""]); // error!
// ---------> ~~
// Type 'string' is not assignable to type 'number'.

И вы можете видеть дилемму ... иногда люди хотят, чтобы T был союзом, в других случаях люди хотят ограничить вызовы однородными массивами.


В этом случае я 'Я бы предложил изменить тип fns на что-то, для чего компилятор может предложить более широкий универсальный тип. Например:

function getX<F extends Array<() => any>>(fns: F, returns: ReturnType<F[number]>) {
  const c: C<F[number], ReturnType<F[number]>> = {
    fns,
    returns,
  };
  return c;
}

Здесь универсальный тип F должен быть любым, совпадающим с массивом функций без аргументов. Это самая простая вещь, которую можно вывести из типа fns. Как только это удастся, вы можете синтезировать свой собственный тип для returns как ReturnType<F[number]>.

Теперь все должно работать так, как вы ожидаете, я думаю:

getX([b, a], "somen"); // okay
getX([b, a], "true"); // error!
// --------> ~~~~~~
// "true" is not assignable to true | "somen".

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...