web-dev-qa-db-fra.com

TypeScript prend-il en charge le?? opérateur? (Et comment s'appelle-t-il?)

Est-ce que TypeScript supporte actuellement l'opérateur = navigation sécurisée de ?.

c'est à dire:

var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;

En outre, existe-t-il un nom plus commun pour cet opérateur (il est extrêmement difficile de rechercher sur Google).

224
Marty Pitt

Je ne trouve aucune référence à cela dans le spécification du langage TypeScript .

En ce qui concerne l'appel de cet opérateur dans CoffeeScript, il s'appelle l'opérateur existentiel (plus précisément, la "variante d'accès" de l'opérateur existentiel).

De documentation de CoffeeScript sur les opérateurs :

La variante d'accès de l'opérateur existentiel ?. peut être utilisée pour absorber les références nulles dans une chaîne de propriétés. Utilisez-le à la place de l'accesseur de points . dans les cas où la valeur de base peut être null ou non défini .

Ainsi, la variante d'accès de l'opérateur existentiel semble être la bonne façon de se référer à cet opérateur; et TypeScript ne semble pas le supporter actuellement (bien que d’autres ont exprimé le désir de cette fonctionnalité ).

91
Donut

Pas aussi beau qu'un single?, Mais ça marche:

var thing = foo && foo.bar || null;

Vous pouvez utiliser autant de && que vous le souhaitez:

var thing = foo && foo.bar && foo.bar.check && foo.bar.check.x || null;
126
A. K-R

Ceci est défini dans la spécification de chaînage facultatif ECMAScript, nous devrions donc probablement faire référence à chaînage facultatif lorsque nous en discuterons. Implémentation probable:

const result = a?.b?.c;

En résumé, l'équipe TypeScript attend que la spécification ECMAScript soit plus stricte, de sorte que sa mise en œuvre risque de ne pas être perturbée à l'avenir. S'ils mettaient en œuvre quelque chose maintenant, il faudrait des changements majeurs si ECMAScript redéfinissait leurs spécifications.

Voir Spécification de chaînage optionnel

Là où quelque chose ne sera jamais du code JavaScript standard, l'équipe TypeScript peut mettre en œuvre à sa guise, mais pour les ajouts futurs à ECMAScript, elle souhaite préserver la sémantique même si elle permet un accès anticipé, comme c'est le cas pour de nombreuses autres fonctionnalités.

Coupes courtes

Ainsi, tous les opérateurs géniaux JavaScripts sont disponibles, y compris les conversions de types telles que ...

var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool

Solution manuelle

Mais revenons à la question. J'ai un exemple obtus de la façon dont vous pouvez faire une chose similaire en JavaScript (et donc en TypeScript) bien que je ne suggère absolument pas que ce soit une fonction aussi élégante que celle que vous recherchez vraiment.

(foo||{}).bar;

Donc, si foo est undefined le résultat est undefined et si foo est défini et possède une propriété nommée bar ayant une valeur, le résultat est cette valeur .

Je mets un exemple sur JSFiddle .

Cela semble assez sommaire pour des exemples plus longs.

var postCode = ((person||{}).address||{}).postcode;

Fonction de la chaîne

Si vous êtes désespéré pour une version plus courte alors que la spécification est encore en suspens, j'utilise cette méthode dans certains cas. Il évalue l'expression et renvoie une valeur par défaut si la chaîne ne peut pas être satisfaite ou termine par null/indéfini (remarquez que le != est important ici, nous ne le faisons pas veut utiliser !== comme on veut un peu de jonglage positif ici).

function chain<T>(exp: () => T, d: T) {
    try {
        let val = exp();
        if (val != null) {
            return val;
        }
    } catch { }
    return d;
}

let obj1: { a?: { b?: string }} = {
    a: {
        b: 'c'
    }
};

// 'c'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {
    a: {}
};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = null;

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));
66
Fenton

Il existe une demande de fonctionnalité ouverte sur github où vous pouvez exprimer votre opinion/désir: https://github.com/Microsoft/TypeScript/issues/16

30
basarat

Edit: J'ai mis à jour la réponse grâce au commentaire fracz.

TypeScript 2.0 publié !. Ce n'est pas la même chose que ?. (Safe Navigator en C #)

Voir cette réponse pour plus de détails:

https://stackoverflow.com/a/38875179/1057052

Cela indiquera seulement au compilateur que la valeur n'est pas nulle ou indéfinie. Ceci ne vérifiera pas si la valeur est null ou non définie.

opérateur d'assertion TypeScript non NULL

// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
    // Throw exception if e is null or invalid entity
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // Assert that e is non-null and access name
}
22
Jose A

L'opérateur ?. n'est pas pris en charge dans TypeScript version 2..

J'utilise donc la fonction suivante:

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

l'utilisation ressemble à ceci:

o(o(o(test).prop1).prop2

de plus, vous pouvez définir une valeur par défaut:

o(o(o(o(test).prop1).prop2, "none")

Cela fonctionne très bien avec IntelliSense dans Visual Studio.

8
VeganHunter

Nous avons créé cette méthode util en travaillant sur Phonetradr , ce qui peut vous donner un accès digne de ce type aux propriétés profondes avec TypeScript:

/**
 * Type-safe access of deep property of an object
 *
 * @param obj                   Object to get deep property
 * @param unsafeDataOperation   Function that returns the deep property
 * @param valueIfFail           Value to return in case if there is no such property
 */
export function getInSafe<O,T>(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T {
    try {
        return unsafeDataOperation(obj)
    } catch (error) {
        return valueIfFail;
    }
}

//Example usage:
getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, '');

//Example from above
getInSafe(foo, x => x.bar.check, null);
3
phidias

Je ne recommande généralement pas cette approche (faites attention aux problèmes de performances), mais vous pouvez utiliser l'opérateur spread pour cloner un objet en profondeur, sur lequel vous pouvez ensuite accéder à la propriété.

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.firstName;

Cela fonctionne parce que le type de 'firstName' est 'propagé' à travers.

Je l'utilise le plus souvent lorsque j'ai une expression find(...) qui peut renvoyer null et que j'ai besoin d'une propriété unique:

 // this would cause an error (this ID doesn't exist)
 const people = [person];
 const firstName2 = people.find(p => p.personId == 999).firstName;

 // this works - but copies every property over so raises performance concerns
 const firstName3 = { ...people.find(p => p.personId == 999) }.firstName;

Il peut y avoir des cas Edge avec la façon dont TypeScript infère les types et cela ne compilera pas, mais cela devrait généralement fonctionner.

2
Simon_Weaver

Comme indiqué précédemment, il est toujours en cours d'examen, mais il est mort dans l'eau depuis quelques années maintenant.

En vous appuyant sur les réponses existantes, voici la version la plus concise du manuel à laquelle je puisse penser:

jsfiddle

function val<T>(valueSupplier: () => T): T {
  try { return valueSupplier(); } catch (err) { return undefined; }
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(val(() => obj1.a.b)); // 'c'

obj1 = { a: {} };
console.log(val(() => obj1.a.b)); // undefined
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

Il échoue simplement en silence sur les erreurs de propriété manquantes. Il revient à la syntaxe standard pour déterminer la valeur par défaut, qui peut également être omise complètement.


Bien que cela fonctionne pour des cas simples, si vous avez besoin de tâches plus complexes telles que l'appel d'une fonction puis l'accès à une propriété sur le résultat, toutes les autres erreurs sont également avalées. Mauvais design.

Dans le cas ci-dessus, une version optimisée de l'autre réponse postée ici constitue la meilleure option:

jsfiddle

function o<T>(obj?: T, def: T = {} as T): T {
    return obj || def;
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(o(o(o(obj1).a)).b); // 'c'

obj1 = { a: {} };
console.log(o(o(o(obj1).a)).b); // undefined
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

Un exemple plus complexe:

o(foo(), []).map((n) => n.id)

Vous pouvez également aller dans le sens inverse et utiliser quelque chose comme Lodash ' _.get() . C'est concis, mais le compilateur ne pourra pas juger de la validité des propriétés utilisées:

console.log(_.get(obj1, 'a.b.c'));
0
Benny Bottema

_.get(obj, 'address.street.name') fonctionne très bien pour JavaScript où vous n'avez pas de types. Mais pour TypeScript il nous faut le véritable opérateur Elvis!

0
Angelos Pikoulas