Почему оператор && выдает тип второго операнда
-
12-12-2019 - |
Вопрос
Спецификация TypeScript гласит в §4.15.6 о &&
оператор:
Оператор && допускает, чтобы операнды были любого типа, и выдает результат того же типа, что и второй операнд.
В Javascript &&
operator возвращает первый операнд, если он ложный, в противном случае он возвращает второй операнд (см. ECMA-262, §11.11).
Это означает, что если левый операнд является ложным, &&
вернет значение, соответствующее типу левого операнда.Например,
typeof ( false && {} ) === "boolean" // true
typeof ( '' && 1 ) === "string" // true
typeof ( null && "hello" ) === "object" // true
typeof ( NaN && true ) === "number" // true
Машинописный текст, согласно приведенному выше правилу, будет неправильно предсказать, какие типы приведенных выше выражений будут Object
, Number
, String
и Boolean
, соответственно.
Я что-то упускаю?Есть ли веская причина для создания типа &&
выражение соответствует типу второго операнда?Разве тип результата не должен вести себя как ||
operator, и возвращает наилучший общий тип из двух операндов, и Any
если нет наилучшего общего типа?
Решение
Короче говоря, здесь нет решения, которое устраивало бы всех.
Рассмотрим эту распространенную идиому:
var customer = GetCustomer(...); // of type 'Customer'
var address = customer && customer.address;
if(address) {
printAddressLabel(address); // Signature: (Address) => void
} else {
// Couldn't find the customer or the customer has no address on file
}
Было бы довольно неубедительно сдаться и решить, что "адрес" - это "любой", потому что нет наилучшего общего типа между Клиентом и Адресом.
В большинстве случаев, когда используется оператор &&, либо типы уже сопоставление, или && используется способом объединения значений, как описано выше.В любом случае, возвращая тип правильного операнда, пользователь получает ожидаемый тип.
Хотя на данный момент безопасность типов технически нарушена, она делает это не таким образом, который мог бы привести к ошибке.Либо вы собираетесь проверить результирующее значение на истинность (в этом случае тип более или менее не имеет значения), либо вы собираетесь использовать предполагаемый правый операнд для какой-либо операции (приведенный выше пример выполняет и то, и другое).
Если мы посмотрим на перечисленные вами примеры и представим, что левый операнд является неопределенно истинным или ложным, а затем попытаемся написать нормальный код, который будет работать с возвращаемым значением, все станет намного понятнее - вы просто мало что можете делай с 'false && {}' это еще не входит в "любую" позицию аргумента или тест на правдивость.
Добавление
Поскольку некоторых людей вышесказанное не убедило, вот другое объяснение.
Давайте на мгновение представим, что система типов TypeScript добавила три новых типа: Truthy<T>
, Falsy<T>
, и Maybe<T>
, представляющий возможные истинные / ложные значения типа T
.Правила для этих типов следующие:
Truthy<T>
ведет себя точно так же, какT
- Вы не можете получить доступ ни к каким свойствам
Falsy<T>
- Выражение типа
Maybe<T>
, при использовании в качестве условия вif
блок, становитсяTruthy<T>
в теле того самогоif
блок иFalsy<T>
вelse
блок
Это позволило бы вам делать подобные вещи:
function fn(x: Maybe<Customer>) {
if(x) {
console.log(x.address); // OK
} else {
console.log(x.phone); // Error: x is definitely falsy
}
console.log(x.name); // Warning: x might be falsy!
}
Пока довольно неплохо.Теперь мы можем выяснить, каковы правила ввода типов для оператора && .
Truthy<T> && x
должна быть ошибка - если известно, что левая сторона правдива, вы должны были просто написатьx
Falsy<T> && x
должна быть ошибка - если известно, что левая сторона является ложной,x
является недоступным кодомMaybe<T> && x
должен производить...что?
Мы знаем результат Maybe<T> && x
будет либо ложным значением типа T
, или x
.Он не может произвести Truthy<T>
(если только T
== тип x
в этом случае вся эта дискуссия является спорной).Давайте назовем этот новый тип Falsy<T> XOR Maybe<U>
.
Какими должны быть правила Falsy<T> XOR Maybe<U>
быть?
- Очевидно, что вы не можете использовать свойства
T
на нем.Если значение имеет типT
, это фальшивка и небезопасна для использования. - Вы должны быть в состоянии использовать его в качестве
Maybe<U>
, посколькуFalsy<T>
иFalsy<U>
вести себя таким же образом - Вы не должны иметь возможности использовать свойства
U
, потому что значение все еще может быть ложным. - Если вы используете его в
if
тест, тогда он должен статьTruthy<U>
в блоке этогоif
заявление
Другими словами, Falsy<T> XOR Maybe<U>
является Maybe<U>
.Он следует все тем же правилам.Вам вообще не нужно усложнять систему типов, добавляя это странное XOR
введите, потому что тип, соответствующий всем необходимым вам спецификациям, уже существует.
Это немного похоже на то, как если бы вы дали кому-то коробку и сказали: "Это либо пустая коробка для мусора, либо полная коробка вторсырья".Вы можете смело выбрасывать содержимое коробки в мусорное ведро.