¿Por qué el operador && produce el tipo del segundo operando?
-
12-12-2019 - |
Pregunta
La especificación TypeScript establece en §4.15.6 sobre la &&
operador:
El operador && permite que los operandos sean de cualquier tipo y produce un resultado del mismo tipo que el segundo operando.
En Javascript, el &&
El operador devuelve el primer operando si es falso; de lo contrario, devuelve el segundo operando (ver ECMA-262 §11.11).
Eso significa que si el operando izquierdo es falso, &&
devolverá un valor que coincida con el tipo del operando izquierdo.Por ejemplo,
typeof ( false && {} ) === "boolean" // true
typeof ( '' && 1 ) === "string" // true
typeof ( null && "hello" ) === "object" // true
typeof ( NaN && true ) === "number" // true
La escritura mecanografiada, de acuerdo con la regla citada anteriormente, incorrectamente predecir que los tipos de las expresiones anteriores serán Object
, Number
, String
y Boolean
, respectivamente.
¿Me estoy perdiendo de algo?¿Existe una buena razón para hacer que el tipo de &&
¿La expresión coincide con el tipo del segundo operando?¿No debería comportarse el tipo de resultado como el ||
operador y devolver el mejor tipo común de los dos operandos, y Any
si no existe el mejor tipo común?
Solución
En pocas palabras, aquí no existe una solución que satisfaga a todos.
Considere este modismo común:
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
}
Sería bastante tonto darse por vencido y decidir que "dirección" es "cualquiera" porque no existe el mejor tipo común entre Cliente y Dirección.
En la mayoría de los casos en los que se utiliza el operador &&, ya sea los tipos ya coincide, o && se utiliza de manera fusionante de valores como se muestra arriba.En cualquier caso, devolver el tipo del operando correcto le da al usuario el tipo esperado.
Si bien la seguridad de tipos técnicamente está fallando en este punto, no lo hace de una manera que pueda resultar en un error.O vas a probar la veracidad del valor resultante (en cuyo caso el tipo es más o menos irrelevante), o vas a utilizar el operando presunto correcto para alguna operación (el ejemplo anterior hace ambas cosas).
Si miramos los ejemplos que enumeró y pretendemos que el operando izquierdo es indeterminadamente verdadero o falso y luego intentamos escribir un código sensato que funcione con el valor de retorno, se vuelve mucho más claro: simplemente no hay mucho que pueda hacer. hacer con 'false && {}' que aún no esté en una posición de argumento o prueba de veracidad de 'cualquier'.
Apéndice
Dado que a algunas personas no les convenció lo anterior, aquí hay una explicación diferente.
Supongamos por un momento que el sistema de tipos TypeScript agregó tres tipos nuevos: Truthy<T>
, Falsy<T>
, y Maybe<T>
, que representa posibles valores verdaderos/falsos de tipo T
.Las reglas para estos tipos son las siguientes:
Truthy<T>
se comporta exactamente comoT
- No puedes acceder a ninguna propiedad de
Falsy<T>
- Una expresión de tipo.
Maybe<T>
, cuando se utiliza como condición en unif
bloque, se convierte enTruthy<T>
en el cuerpo de ese mismoif
bloque y unFalsy<T>
en elelse
bloquear
Esto te permitiría hacer cosas como esta:
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!
}
Bastante bien hasta ahora.Ahora podemos descubrir cuáles son las reglas de tipo para el operador &&.
Truthy<T> && x
debería haber un error: si se sabe que el lado izquierdo es verdadero, debería haber escritox
Falsy<T> && x
debería ser un error: si se sabe que el lado izquierdo es falso,x
es un código inalcanzableMaybe<T> && x
debería producir...¿qué?
conocemos el resultado de Maybe<T> && x
será un valor falso de tipo T
, o x
.No puede producir Truthy<T>
(a menos que T
== el tipo de x
en cuyo caso toda esta discusión es discutible).Llamemos a este nuevo tipo Falsy<T> XOR Maybe<U>
.
¿Cuáles deberían ser las reglas de Falsy<T> XOR Maybe<U>
¿ser?
- Claramente, no puedes usar propiedades de
T
en eso.Si el valor es de tipoT
, es falso y no es seguro de usar. - Deberías poder utilizarlo como
Maybe<U>
, desdeFalsy<T>
yFalsy<U>
tener los mismos comportamientos - No deberías poder usar propiedades de
U
, porque el valor aún podría ser falso. - Si lo usas en un
if
prueba, entonces debería convertirse en unaTruthy<U>
en el bloque de eseif
declaración
En otras palabras, Falsy<T> XOR Maybe<U>
es Maybe<U>
.Sigue todas las mismas reglas.No necesitas complicar el sistema de tipos en absoluto agregando este extraño XOR
tipo, porque ya existe un tipo que se ajusta a todas las especificaciones que necesita.
Esto es un poco como darle a alguien una caja y decirle: "Esta es una caja de basura vacía o una caja llena de materiales reciclables".Puedes vaciar de forma segura el contenido de la caja en el contenedor de reciclaje.