Почему аргумент типа не выводится как тип объединения?

Этот код

declare function fn<T, U>(array: T[], predicates: ((arg: T) => U)[]): [T, U];
let a = fn([1, 2, 3], [x => 2, x => 's']);

приводит к этой ошибке:

Аргумент типа для параметра типа «U» не может быть выведен из использования. Рассмотрите возможность явного указания аргументов типа. Аргумент-кандидат типа «число» не является допустимым аргументом типа, потому что он не является супертипом кандидата «строка». функция fn (массив: T [], предикаты: ((arg: T) => U) []): [T, U]

Почему нельзя U просто предположить, что здесь имеется тип string | number?


person thorn0    schedule 06.10.2016    source источник


Ответы (1)


TypeScript вообще не будет синтезировать тип объединения во время общего вывода. Проще говоря, причина в том, что делать такой вывод нежелательно:

function compare<T>(x: T, y: T): number { ... }
// Could infer T: string | number here... but that'd be bad
compare('oops', 42);

Если универсальный тип не может быть сформирован путем выбора одного из кандидатов на вывод, вы получите сообщение об ошибке.

Опыт подсказал этот выбор. В предыдущих версиях (до того, как существовали типы объединения), {} выводился бы, если ни один из кандидатов вывода не был супертипом всех кандидатов. На практике это привело к множеству пропущенных ошибок, похожих на приведенный выше пример.

person Ryan Cavanaugh    schedule 06.10.2016
comment
Имеет смысл. Но с другой стороны, если мы сделаем predicates просто U[], предполагается, что U будет типом объединения. - person thorn0; 07.10.2016
comment
Почему к U[] и ((arg: T) => U)[] относятся по-разному? - person thorn0; 07.10.2016
comment
U[] оказывается единственным кандидатом на вывод, тип которого берется из типа элемента массива. Гетерогенные массивы раньше запрещались примерно по той же логике, но теперь они разрешены, потому что типы объединения обычно хорошо с ними работают. - person Ryan Cavanaugh; 07.10.2016
comment
((arg: T) => U)[] - тоже своего рода неоднородный массив, не так ли? Для меня это больше похоже на случай U[], чем на функцию compare из вашего первоначального ответа - person thorn0; 07.10.2016
comment
Я могу :-) ((arg: T) => number) | ((arg: T) => string) нельзя объединить в (arg: T) => number | string во время вывода для аргумента универсального типа, потому что он превратит возвращаемый тип в тип объединения. Теперь вопрос в том, имеет ли смысл ослаблять это ограничение, если у нас уже есть тип объединения для начала? - person artem; 07.10.2016
comment
@RyanCavanaugh По умолчанию это имеет смысл, но есть ли способ сказать TypeScript, что на самом деле, пожалуйста, синтезируйте здесь тип объединения во время общего вывода? (Кстати, спасибо за вашу работу над TypeScript, вы классный!) - person Han Seoul-Oh; 19.07.2017
comment
@RyanCavanaugh Что меня действительно удивило, так это то, что я не мог даже использовать перегрузку типов, чтобы вручную предложить синтезировать тип объединения: git.io/v7eO2 - person Han Seoul-Oh; 19.07.2017
comment
@RyanCavanaugh Почему мы не видим такого же поведения, когда параметры являются объектами (а не примитивами, как в примере выше)? typescript-play.js.org/#code/ - person Oliver Joseph Ash; 12.06.2019