javascript - Remplacement de l'opérateur Elvis d'Angular2 en Typescript


Y a-t-il un opérateur dans TypeScript qui est utilisé de manière similaire à l'opérateur Elvis d'Angular2, je veux dire, supposons que je doive obtenir une clé d'un objet comme ceci :

this.myForm.name.first_name

et au cas où first_name n'existerait pas, cela générera une erreur first_name of undefined,

oui je peux gérer cette erreur en utilisant l'opérateur ternaire de TypeScript comme ceci

this.myForm.name ? this.myForm.name.first_name : ''

mais parfois les clés deviennent trop longues,

Alors, y a-t-il un opérateur comme Elvis Operator d'Angular2 en TypeScript pour que je puisse l'utiliser comme ceci

this.myForm?.name?.first_name

Réponse #1

Mise à jour décembre 2019 : TypeScript 3.7 a introduit le chaînage optionnel, qui équivaut à l'opérateur de navigation sûre connu dans d'autres langages. La proposition ECMAScript pour le chaînage optionnel a atteint le stade 4 et fera donc partie de la spécification dans ES2020. Voir mdn : Chaînage optionnel pour plus d'informations.

Mise à jour juillet 2017 : Comme l'a souligné JGFMK dans les commentaires, il existe une proposition ECMAScript appelée Chaînage optionnel pour JavaScript. Si/lorsque la proposition atteint le Stade 4, elle sera ajoutée à la spécification du langage.

Il n'y a ni navigation sécurisée ni opérateur Elvis en TypeScript et, autant que je sache, rien de comparable non plus.

Pour référence, voir la demande de fonctionnalité à Suggestion : « opérateur de navigation sécurisée », c.-à-d. x?.y. L'explication pour ne pas l'implémenter est la suivante (ce qui, à mon avis, est une raison valable) :

Je ferme ceci pour le moment. Puisqu'il n'y a pas vraiment quelque chose de spécifique à TypeScript qui nécessiterait cela au niveau de l'expression, ce genre de grand changement d'opérateur devrait se produire au comité de spécification ES plutôt qu'ici.

Les déclencheurs généraux pour réévaluer cela seraient une proposition concrète d'ES atteignant la prochaine étape, ou un consensus général du comité ES que cette fonctionnalité ne se produirait pas avant longtemps (afin que nous puissions définir notre propre sémantique et être raisonnablement sûrs qu'elle "prévaudrait").

Des alternatives à cette notation seraient d'utiliser l'opérateur logique ET, try/catch ou une fonction d'assistance comme getSafe(() => this.myForm.name.first_name) comme décrit dans ce post.

Réponse #2

Faites un OU répété avec un objet vide

Je ne peux pas vivre sans Elvis lorsque j'écris du HTML qui fait référence à des propriétés/sous-propriétés qui peuvent ou non exister.

Nous ne pouvons pas écrire

this.myForm?.name?.first_name

Je suppose que les personnes responsables d'ES passent probablement la majeure partie de leur temps à écrire réellement du JS, et non à écrire des morceaux de HTML front-end qui accèdent aux propriétés des objets, d'où leur étonnement de se demander pourquoi quelqu'un aurait besoin de navigation sécurisée.

Ils soutiennent que « Personne n'en a besoin », « Cela permet aux erreurs de passer inaperçues et pourrait donc te causer des problèmes plus tard » et « Comment interpréteriez-vous x?(y) ? »

Ma solution, pendant que j'attends qu'une future spécification ES inclue l'opérateur Elvis, est la suivante :

(((this.myForm || {}).name || {}).first_name )

En bref, à chaque étape où une valeur pourrait être undefined, vous la OUz avec un {}, de sorte que `undefined` devienne un objet vide. Une fois que vous mettez tout cela entre parenthèses, vous pouvez essayer d’extraire une propriété de celui-ci en toute sécurité, puisque toute propriété que vous essayez d’extraire de {} sera simplement undefined plutôt qu’une erreur réelle.

Cela devient un peu moche quand vous avez plusieurs niveaux, mais dans de nombreux cas, c'est moins moche que d'avoir à utiliser une série de && pour descendre progressivement à travers les niveaux de l'objet.

Pour l'accès au tableau, faites un OU avec un tableau vide

Si vous naviguez dans un tableau, par exemple si le formulaire contenait de nombreux noms et que vous vouliez sélectionner le premier, l'approche est similaire :

((((this.myForm || {}).names || [])[0] || {}).first_name )

Que se passe-t-il si quelque chose dans la chaîne est 0 ou "" ?

Supposons que vous vous attendiez à lire une valeur "John" à partir de this.myForm.names[0].first_name.

La situation typique qui déclencherait normalement une erreur est celle où il n'y a tout simplement pas d'objet this.myForm. Au lieu de provoquer une erreur, la formule que j'ai décrite ci-dessus fonctionnerait et serait interprétée comme {}.first_name, ce qui est undefined (mais pas une erreur).

Imaginez maintenant qu’une personne hostile ait discrètement défini this.myForm à 0 ou "" ou {}. Verrons-nous maintenant une erreur ? Non, parce que les valeurs 0 ou "" sont considérées comme fausses par Javascript, et {} est vrai mais est lui-même {}, donc this.myForm || {} s’évalue à {}, et le reste de la chaîne tombe sur les valeurs par défaut comme avant, et la méthode fonctionne toujours, retournant undefined au lieu d’une erreur.

Exemple

console.log("Each example shows first the result WITH the workaround, and then WITHOUT.")

console.log("a. This should work normally.")
a = { myForm:{ names:[ { first_name:"John", surname:"Smith"}]} };
console.log(a.myForm.names[0].first_name)
console.log ((((a.myForm || {}).names || [])[0] || {}).first_name )

console.log("b. We have removed the first_name property. This still works.")
b = { myForm:{ names:[ {                    surname:"Smith"}]} };
console.log(b.myForm.names[0].first_name)
console.log ((((b.myForm || {}).names || [])[0] || {}).first_name )

console.log("c. We have removed the entire 'names' array. This is fixed by the workaround, but gives an error if run without the workaround.")
c = { myForm:{ } };
console.log ((((c.myForm || {}).names || [])[0] || {}).first_name )
console.log(c.myForm.names[0].first_name)

    console.log("d. Now the whole root object is empty. Again the workaround gets around this, but without the workaround, we get an error.")
    d = { };
    console.log ((((d.myForm || {}).names || [])[0] || {}).first_name )
    console.log(d.myForm.names[0].first_name)