Подтвердить что ты не робот

Почему оператор && создает тип второго операнда

Спецификация TypeScript заявляет в §4.15.6 о &&:

& & оператор допускает, чтобы операнды были любого типа и выдавали результат того же типа, что и второй операнд.

В Javascript оператор && возвращает первый операнд, если он является ложным, иначе он возвращает второй операнд (см. ECMA-262 §11.11).

Это означает, что если левый операнд ложный, && вернет значение, соответствующее типу левого операнда. Например,

typeof ( false && {}      ) === "boolean" // true
typeof ( ''    && 1       ) === "string"  // true
typeof ( null  && "hello" ) === "object"  // true
typeof ( NaN   && true    ) === "number"  // true

Typescript, согласно приведенному выше правилу, неправильно предсказывал бы типы вышеуказанных выражений Object, Number, String и Boolean соответственно.

Я что-то упустил? Есть ли веская причина, чтобы тип выражения && соответствовал типу второго операнда? Должен ли тип результата вести себя как оператор || и возвращать наилучший общий тип двух операндов и Any, если нет лучшего общего типа?

4b9b3361

Ответ 1

Короче говоря, здесь нет решения, которое понравится всем.

Рассмотрим эту общую идиому:

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, потому что тип, который соответствует всем требуемым спецификациям, уже существует.

Это немного похоже на то, чтобы дать кому-то коробку и сказать: "Это либо пустой ящик мусора, либо полный ящик вторсырья". Вы можете безопасно удалить содержимое коробки в мусорный ящик.