В TypeScript у меня есть API, входные данные которого представляют собой объединение кортежей, и у меня есть значение, тип которого представляет собой кортеж объединений, который я хочу передать в него. Кажется, что это работает в большинстве случаев, но я не могу понять, почему это не работает в моем случае и что я могу сделать, чтобы заставить его работать без необходимости утверждения типа. Вот репродукция выпуска (ссылка TS Playground):
(x: [1 | 2]): [1] | [2] => x; // OK
(x: [1 | 2]): [1 | 11] | [2 | 22] => x; // OK
(x: [1 | 2]): [1 | []] | [2 | 22] => x; // OK
(x: [1 | 2]): [1 | []] | [2 | []] => x; // ERROR
Несмотря на то, что первые три примера в порядке, четвертый пример выдает ошибку:
Type '[1 | 2]' is not assignable to type '[1 | []] | [2 | []]'.
Type '[1 | 2]' is not assignable to type '[1 | []]'.
Type '1 | 2' is not assignable to type '1 | []'.
Type '2' is not assignable to type '1 | []'.
Вообще говоря, TypeScript не распределяет автоматически все операторы ковариантного типа по типам-объединениям. Итак, {a: X | Y}
не становится {a: X} | {a: Y}
, а [X | Y]
не становится [X] | [Y]
. Выполнение чего-то подобного в целом вызвало бы взрыв типов объединений (представьте, чем бы стал [X | Y, T | U | V | W, Q | R | S]
). Он также не определяет автоматически, будут ли два типа совместимы, если каждый из них будет распространяться таким образом. Компилятору придется проделать большую работу, но польза от этого будет ограничена. До TypeScript 3.5 все ваши примеры были ошибками.
В TypeScript 3.5 введена так называемая «умная» проверка типов объединения , реализованная в microsoft/TypeScript#30779 , в которой вышеупомянутое распределение действительно имеет место, но только если рассматриваемые типы объединения являются различаемыми объединениями и только в том случае, если рассматриваемые профсоюзы будут иметь 25 или менее членов. Эти ограничения накладывают ограничения на объем дополнительной работы, которую TypeScript необходимо затрачивать для проверки этих вещей, но также приводят к поведению, которое, по мнению разработчиков, может быть непоследовательным или произвольным.
Итак, рассматривая
(x: [1 | 2]): [1] | [2] => x; // OK
(x: [1 | 2]): [1 | 11] | [2 | 22] => x; // OK
(x: [1 | 2]): [1 | []] | [2 | 22] => x; // OK
(x: [1 | 2]): [1 | []] | [2 | []] => x; // ERROR
В первых трех случаях типы объединения [1] | [2]
, [1 | 11] | [2 | 22]
и [1 | []] | [2 | 22]
считаются распознаваемыми объединениями. Элемент кортеж (свойство по индексу "0"
) представляет собой литеральный тип или объединение литеральных типов хотя бы в одном члене объединения. Но тип [1 | []] | [2 | []]
не считается распознаваемым объединением. Пустой тип кортежа []
не считается литеральным типом, и поскольку он существует в каждом члене объединения и ни один член объединения не состоит только из литеральных типов, он не соответствует критериям.
Это означает, что «умная» проверка типа объединения происходит в первых трех примерах, но не происходит в последнем примере.