casting - ¿Cómo se puede forzar a Flow a convertir un valor en otro tipo?
flowtype (3)
¿Es posible lanzar por la fuerza una variable en el flujo ?
type StringOrNumber = string | number
const foo: StringOrNumber = ''hello''
// I look for something like `const bar:string = (string) foo`
const bar: string = foo // fails
const bar: string = (foo: string) // also fails
En el ejemplo que da, está viendo un "reparto" de un tipo de unión a uno de sus miembros. Si bien es común pensar en esto como un reparto, no es lo mismo que un reparto de tipos en otros idiomas.
Al establecer el tipo de foo
a string | number
string | number
, le hemos dicho a Flow que este valor podría ser una cadena o un número. Entonces, sucede que colocamos una cadena, pero Flow no descarta nuestra afirmación directa sobre su tipo debido a eso, incluso en situaciones (como esta) en las que no podría cambiar más adelante.
Para asignarlo a una variable de tipo string
, Flow necesita saber que, aunque podría haber sido una string
o un number
, para cuando realicemos la asignación, estamos seguros de que solo puede ser una string
.
Este proceso de reducción de las posibles opciones se denomina refinamiento de tipo .
Refinamientos de tipo
Para refinar el tipo, debemos demostrar que debe ser del tipo que decimos que es, de una manera que Flow entiende .
En el ejemplo original, puedes hacer esto usando typeof
:
type StringOrNumber = string | number
const foo: StringOrNumber = ''hello''
// This would raise an error here:
// const bar: string = foo
if (typeof foo === "string") {
// Flow now knows that foo must be a string, and allows this.
const bar: string = foo
}
Flow no entiende todo lo que un humano puede ver como un refinamiento de tipo, por lo que a veces tendrá que mirar los documentos de refinamiento para ver qué podría hacer que Flow lo entienda.
Comentarios de supresión
A veces no hay forma de expresar la seguridad de un refinamiento a Flow. Podemos obligar a Flow a aceptar una declaración mediante el uso de un comentario de supresión , que suprimirá un error que, de lo contrario, informaría. El comentario de supresión predeterminado es $FlowFixMe
, pero se puede configurar para un comentario o comentarios diferentes.
Flow informará de un error en la segunda línea de esto, informando que unionValue podría ser de tipo ''número'':
const unionValue: StringOrNumber = ''seven''
const stringValue: string = unionValue
Sin embargo, al usar un comentario de supresión, esto pasa al flujo:
const unionValue: StringOrNumber = ''seven''
// $FlowFixMe: We can plainly see this is a string!
const stringValue: string = unionValue
Una característica útil de los comentarios de supresión es que un comentario de supresión sin un siguiente error de supresión se considera un error. Si cambiamos el tipo en el ejemplo:
const unionValue: string = ''seven''
// $FlowFixMe: Even though this is a string, suppress it
const stringValue: string = unionValue
Ahora Flow informará un error de "supresión no utilizada" en su lugar, alertándonos. Esto es particularmente útil cuando Flow debería poder reconocer un refinamiento pero no puede. Al usar un comentario de supresión, se nos alerta para eliminar el comentario (y obtener seguridad de tipo adicional) si una versión futura de Flow reconoce el código como tipo de caja fuerte.
Cast-through-any
Si realmente no puede expresarlo de una manera que demuestre que su seguridad fluye, y no puede (o no va a usar) un comentario de supresión, puede enviar cualquier tipo a any
tipo, y any
otro tipo:
const unionValue: StringOrNumber = ''seven''
// Flow will be okay with this:
const stringValue: string = (unionValue: any)
Al asignar un valor a any
le estamos pidiendo a Flow que olvide todo lo que sabe sobre el tipo de valor y asuma que todo lo que hagamos con él debe ser correcto. Si luego lo colocamos en una variable escrita, Flow asumirá que debe ser correcto.
Precauciones
Es importante tener en cuenta que tanto los comentarios de supresión como los emitidos no son seguros . Anulan el flujo completamente, y felizmente realizarán "lanzamientos" sin sentido:
const notAString: {key: string, key2: number} = {key: ''value'', key2: 123}
// This isn''t right, but Flow won''t complain:
const stringValue: string = (notAString: any)
En este ejemplo, stringValue
mantiene el object
de notAString
, pero Flow está seguro de que es una cadena.
Para evitar esto, use refinamientos que Flow entienda siempre que pueda y evite el uso de otras técnicas de "casting" inseguras.
Esta respuesta es sólo una sugerencia. Al buscar soluciones a problemas de comprobación de tipos relacionados con eventos y HTMLElement, encontré a muchos guardias invocando instanceof.
Para satisfacer las comprobaciones de tipos, acabo de presentar esta protección genérica y la llamé " cast
(que, por supuesto, no la convierte en "cast"), porque de lo contrario mi código se hinchó.
El costo es, por supuesto, en el rendimiento (bastante relevante al escribir juegos, pero creo que la mayoría de los casos de uso se benefician más de los guardias de tipo que de los milisegundos por iteración).
const cast = (type : any, target : any) => {
if (!(target instanceof type)) {
throw new Error(`${target} is not a ${type}`);
}
return target;
}
Usos:
const fooLayer = cast(HTMLCanvasElement, document.getElementById("foo-layer"));
window.addEventListener("click", (ev : Event) =>
console.log(cast(MouseEvent, ev).clientX - cast(HTMLElement, ev.target).offsetLeft,
cast(MouseEvent, ev).clientY - cast(HTMLElement, ev.target).offsetTop));
Flow no hace casting directo de un tipo a otro, pero puedes hacer algo como
const bar: string = (foo: any);
así que conviertes foo
a an any
, porque any
acepta cualquier tipo de valor como entrada. Luego, debido a que any
tipo también le permite leer todos los tipos posibles de él, puede asignar el valor de any
valor a la bar
porque any
es también una string
.