Est-il possible de transtyper de force une variable dans Flow ?
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
Flow ne fait pas de cast direct d'un type à un autre, mais vous pouvez faire quelque chose comme
const bar: string = (foo: any);
vous convertissez donc foo
en any
, car any
accepte tout type de valeur en entrée. Ensuite, étant donné que le type any
vous permet également d'en lire tous les types possibles, vous pouvez affecter la valeur any
à bar
car un any
est également un string
.
Dans l'exemple que vous donnez, vous regardez une "distribution" d'un type d'union à l'un de ses membres. Bien qu'il soit courant de considérer cela comme un casting, ce n'est pas la même chose que le casting de type dans d'autres langues.
En définissant le type de foo
sur string | number
, nous avons indiqué à Flow que cette valeur pouvait être une chaîne ou un nombre. Nous arrivons alors à y mettre une chaîne, mais Flow ne rejette pas notre assertion directe sur son type à cause de cela, même dans des situations (comme celle-ci) où elle ne pourrait pas changer plus tard.
Pour l'affecter à une variable de type string
-, Flow doit savoir que même s'il peut s'agir d'un string
ou number
, au moment où nous faisons l'affectation, nous sont sûrs qu'il ne peut s'agir que d'un string
.
Ce processus de réduction des options possibles est appelé raffinement de type .
Pour affiner le type, nous devons prouver que ce doit être le type que nous disons qu'il est, d'une manière que Flow comprend.
Dans l'exemple d'origine, vous pouvez le faire en utilisant 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
}
Tout ce qu'un humain peut voir comme un raffinement de type n'est pas compris par Flow, donc parfois vous devrez regarder les documents de raffinement pour voir ce qui pourrait le faire comprendre à Flow.
Parfois, il n'y a aucun moyen d'exprimer la sécurité d'un raffinement à Flow. Nous pouvons forcer Flow à accepter une déclaration en utilisant un commentaire de suppression , qui supprimera une erreur que Flow aurait autrement signalée. Le commentaire de suppression par défaut est $FlowFixMe
, mais il peut être configuré pour un ou plusieurs commentaires différents.
Flow signalera une erreur sur la deuxième ligne de ceci, signalant que unionValue peut être de type 'nombre':
const unionValue: StringOrNumber = 'seven'
const stringValue: string = unionValue
Cependant, en utilisant un commentaire de suppression, cela passe Flow:
const unionValue: StringOrNumber = 'seven'
// $FlowFixMe: We can plainly see this is a string!
const stringValue: string = unionValue
Une caractéristique utile des commentaires de suppression est qu'un commentaire de suppression sans erreur suivante à supprimer est considéré comme une erreur. Si nous changeons le type dans l'exemple:
const unionValue: string = 'seven'
// $FlowFixMe: Even though this is a string, suppress it
const stringValue: string = unionValue
Maintenant, Flow signalera une erreur de "suppression inutilisée" à la place, nous alertant. Cela est particulièrement utile lorsque Flow devrait être en mesure de reconnaître un raffinement mais ne peut pas - en utilisant un commentaire de suppression, nous sommes alertés pour supprimer le commentaire (et gagner une sécurité de type supplémentaire) si une future version de Flow reconnaît le code comme type sûr.
Si vous ne pouvez vraiment pas l'exprimer d'une manière qui démontre sa sécurité de flux, et que vous ne pouvez pas (ou ne voulez pas) utiliser un commentaire de suppression, vous pouvez convertir n'importe quel type en any
et any
à n'importe quel type:
const unionValue: StringOrNumber = 'seven'
// Flow will be okay with this:
const stringValue: string = (unionValue: any)
En convertissant une valeur en any
, nous demandons à Flow d'oublier tout ce qu'il sait sur le type de la valeur, et supposons que tout ce que nous faisons avec doit être correct. Si nous le mettons plus tard dans une variable typée, Flow supposera que cela doit être correct.
Il est important de noter que les commentaires de suppression et les transtypages sont dangereux. Ils remplacent complètement Flow et effectueront avec plaisir des "lancers" complètement absurdes:
const notAString: {key: string, key2: number} = {key: 'value', key2: 123}
// This isn't right, but Flow won't complain:
const stringValue: string = (notAString: any)
Dans cet exemple, stringValue
contient le object
de notAString
, mais Flow est sûr qu'il s'agit d'une chaîne.
Pour éviter cela, utilisez des raffinements que Flow comprend chaque fois que vous le pouvez, et évitez d'utiliser les autres techniques de "casting" dangereuses.
Cette réponse n'est qu'une suggestion. En parcourant les solutions aux problèmes de vérification de type liés à Event et HTMLElement, j'ai rencontré beaucoup de gardes invoquant instanceof.
Pour satisfaire les vérifications de type, je viens d'introduire ce garde générique et je l'ai appelé cast
(ce qui n'en fait pas bien sûr un cast), car sinon mon code s'est tellement gonflé.
Le coût est bien sûr lié aux performances (assez pertinent lors de l'écriture de jeux, mais je suppose que la plupart des cas d'utilisation bénéficient plus de gardes de type que de millisecondes par itération).
const cast = (type : any, target : any) => {
if (!(target instanceof type)) {
throw new Error(`${target} is not a ${type}`);
}
return target;
}
Coutumes:
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));