Articolo originale: JavaScript TypeOf – How to Check the Type of a Variable or Object in JS

I tipi di dato ed il controllo di tipo di dato (type checking) sono aspetti fondamentali di ogni linguaggio di programmazione.

Molti linguaggi di programmazione, come Java, sono fortemente tipizzati. Questo significa che una variabile definita con uno specifico tipo può contenere esclusivamente un valore appartenente a tale tipo.

JavaScript, invece, è un linguaggio debolmente tipizzato o dinamicamente tipizzato. Questo significa che una variabile può contenere un valore di qualsiasi tipo. Anche il seguente codice JavaScript può essere eseguito:

let one = 1;
one = 'one';
one = true;
one = Boolean(true);
one = String('It is possible');

Tenendo questo in considerazione, diventa di importanza critica conoscere il tipo di una variabile in qualsiasi momento.

Il tipo di una variabile è determinato dal tipo di valore ad essa assegnato. JavaScript ha uno speciale operatore, denominato typeof, il quale ti permette di conoscere il tipo di qualsiasi valore.

In questo articolo, impareremo come viene utilizzato typeof, insieme ad alcuni tranelli da cui stare in guardia.

I tipi di dato in JavaScript

Diamo una rapida occhiata ai tipi di dato in JavaScript prima di analizzare meglio l'operatore typeof.

Ci sono sette tipi di dato primitivi in JavaScript. Un tipo di dato primitivo è qualsiasi cosa che non è un oggetto. Essi sono:

  1. String (stringa di testo)
  2. Number (numero floating-point in doppia precisione)
  3. BigInt (numero intero di dimensione arbitraria)
  4. Symbol (variabile particolare il cui valore è unico)
  5. Boolean (variabile booleana)
  6. undefined
  7. null

Qualsiasi altra cosa è un oggetto (Object) -  array e funzioni compresi. Un oggetto è una raccolta di coppie chiave-valore.

L'operatore TypeOf di JavaScript

L'operatore typeof considera solo un operando (operatore unario). Valuta il tipo dell'operando e restituisce il risultato come una stringa.

typeof 007;  // restituisce 'number'

Esiste una sintassi alternativa per l'operatore  typeof, che può essere usato come una funzione:

typeof(operand)

Questa sintassi è utile quando vuoi valutare una espressione invece di un singolo valore. Ecco un esempio:

typeof(typeof 007); // restituisce 'string'

Nell'esempio qui sopra, l'espressione typeof 007 valuta come tipo di dato "number" e restituisce la stringa 'number'. typeof('number') quindi fornisce come risultato un tipo di dato 'string'.

Osserviamo un altro esempio  per comprendere l'importanza delle parentesi con l'operatore typeof.

typeof(999-3223); // restituisce, "number"

Se ometti le parentesi, l'espressione restituirà NaN("Not a Number"):

typeof 999-3223; // restituisce, NaN

Questo accade perché, in primo luogo, l'espressione typeof 999 darà come risultato una stringa, "number". Quindi l'espressione "number" - 32223 restituirà  NaN, come accade quando si esegue una operazione di sottrazione tra una stringa ed un numero.

Esempi di TypeOf in JavaScript

I seguenti snippet di codice mostrano il risultato del controllo di tipi di dato di differenti valori utilizzando l'operatore typeof.

typeof 0;  //'number'
typeof +0;  //'number'
typeof -0;  //'number'
typeof Math.sqrt(2);  //'number'
typeof Infinity;  //'number'
typeof NaN;  //'number', anche se risulta Not a Number
typeof Number('100');  //'number', dopo aver convertito la stringa '100' nel tipo Number
typeof Number('freeCodeCamp');  //'number', nonstante questa stringa letterale non possa essere convertita in Number

typeof true;  //'boolean'
typeof false;  //'boolean'
typeof Boolean(0);  //'boolean'

typeof 12n;  //'bigint'

typeof '';  //'string'
typeof 'freeCodeCamp';  //'string'
typeof `freeCodeCamp is awesome`;  //'string'
typeof '100';  //'string'
typeof String(100); //'string'

typeof Symbol();  //'symbol'
typeof Symbol('freeCodeCamp');  //'symbol'

typeof {blog: 'freeCodeCamp', author: 'Tapas A'};  //'object';
typeof ['This', 'is', 101]; //'object'
typeof new Date();  //'object'
typeof Array(4);  //'object'

typeof new Boolean(true);  //'object'; 
typeof new Number(101);  //'object'; 
typeof new String('freeCodeCamp');  //'object';
typeof new Object;  //'object'

typeof alert;  //'function'
typeof function () {}; //'function'
typeof (() => {});  //'function' - è una funzione a freccia, pertanto sono necessarie le parentesi
typeof Math.sqrt;  //'function'

let a;
typeof a;  //'undefined'
typeof b;  //'undefined'
typeof undefined;  //'undefined'

typeof null;  //'object'

La tavola seguente mostra i valori risultanti dal controllo di tipo forniti da typeof:

TIPOVALORE DI RITORNO DI TYPEOF
String'string'
Number'number'
BigInt'bigint'
Symbol'symbol'
Boolean'boolean'
undefined'undefined'
Function object'function'
null'object'(vedi sotto!)
Qualsiasi altro oggetto'object'

Tranelli comuni con typeof

Ci sono dei casi in cui l'operatore  typeof potrebbe non restituire i tipi che ci aspettiamo. Questo può causare confusione ed errori. Ecco alcuni casi:

Il tipo di NaN è Number (numero)

typeof NaN;  //'number', anche se è Not a Number (letteralmente Non un Numero)

Il risultato di typeof NaN è 'number', ovvero numero. Questo è strano, pertanto non dovremmo usare typeof per individuare un valore NaN. Ci sono modi migliori per gestire questa situazione. Li vedremo tra poco.

Il tipo di null è Object (oggetto)

  typeof null;  //'object'

In JavaScript,  typeof null restituisce object (oggetto), il che dà l'errata impressione che null sia un oggetto invece che un tipo di dato primitivo.

Il risultato di typeof null è effettivamente un bug nel linguaggio. C'è stato un tentativo di risolverlo in passato ma è stato abbandonato a causa di problemi di compatibilità con le versioni precedenti di JS.

Il tipo di una variabile non dichiarata è Undefined

Prima dello standard ES6, un controllo di tipo su una variabile non dichiarata forniva come risultato  'undefined'. Ma questo non è un modo sicuro per gestire tale situazione ed evitare errori.

Con lo standard ES6 possiamo dichiarare variabili con visibilità a livello di blocco (un blocco è un pezzo di codice delimitato da {}. La visibilità a livello di blocco significa che la variabile è visibile a tutto il codice inserito tra queste parentesi graffe) con le parole chiave let oppure const. Se utilizzi l'operatore typeof su tali variabili prima che siano inizializzate, otterrai un errore di tipo ReferenceError.

 typeof cat; // ReferenceError
 let cat = 'brownie'; 

Il tipo di una funzione costruttore è object - oggetto

Tutte le funzioni costruttore, ad eccezione del costruttore Function, saranno sempre classificate da typeof come 'object', ovvero oggetto.

typeof new String('freeCodeCamp'); //'object'

Questo può portare ad un po' di confusione, siccome ci si attende che l'operatore restituisca il tipo effettivo del dato (nel caso soprariportato, un tipo di dato string - stringa).

Il tipo di un array è un oggetto

Sebbene tecnicamente corretto, questo potrebbe essere il caso più spiacevole. Vogliamo differenziare tra un array ed un oggetto anche se un array è tecnicamente un oggetto in JavaScript.

typeof Array(4);  //'object'

Fortunatamente ci sono modi per rilevare un array correttamente. Li vedremo tra poco.

Oltre typeofUn migliore controllo del tipo dei dati

Ora che abbiamo conosciuto alcune delle limitazioni che sorgono con l'impiego dell'operatore typeof, osserviamo come porvi rimedio ed implementare un controllo del tipo dei dati più efficace.

Come rilevare NaN

In JavaScript, NaN è un valore speciale. Il valore NaN rappresenta il risultato di una espressione aritmetica che non può essere effettivamente rappresentata. Per esempio:

let result = 0/0;
console.log(result);  // restituisce NaN

Inoltre, se eseguiamo una qualsiasi operazione aritmetica con NaN, questa fornirà sempre come risultato NaN.

console.log(NaN + 3); // restituisce NaN

Il controllo di tipo su NaN eseguito utilizzando l'operatore typeof non aiuta molto, poichè restituisce il tipo 'number', numero. JavaScript ha una funzione globale denominata isNaN() per verificare se un risultato è NaN.

isNaN(0/0); // restituisce true

Ma anche qui sorge un problema:

isNaN(undefined); // restituisce vero per 'undefined'

Nello standard ES6. il metodo isNaN() è aggiunto all'oggetto globale Number. Questo metodo è molto più affidabile e pertanto è quello preferito.

Number.isNaN(0/0); // restituisce true
Number.isNaN(undefined); // restituisce false

Un altro aspetto interessante di  NaN è che l'unico valore di JavaScript che non è mai uguale a nessun altro valore, incluso se stesso. Pertanto questo è un altro metodo per rilevare NaN dove lo standard ES6 non è supportato:

function isNaN (input) {
  return input !== input;
}

Come rilevare null in JavaScript

Abbiamo visto come rilevare null usando l'operatore typeof generi confusione. Il metodo preferito per verificare se qualche valore è uguale a null è utilizzare un operatore di uguaglianza stretta (===).

function isNull(input) {
 return input === null;
}

Accertati di non utilizzare l'operatore di uguaglianza generico == per errore. Usare  == al posto di  === fornirà un risultato fuorviante.

Come rilevare un array in JavaScript

Dall'introduzione dello standard ES6, possiamo rilevare un array utilizzando il metodo Array.isArray .

Array.isArray([]); // restituisce vero
Array.isArray({}); // restituisce falso

Prima di ES6, potevamo usare l'operatore instanceof  per identificare un array:

function isArray(input) {
  return input instanceof Array;
}

Una soluzione generale al controllo del tipo dei dati in JavaScript

Esiste un modo con cui possiamo creare una soluzione generale al problema del controllo del tipo dei dati. Diamo un occhiata al metodo Object.prototype.toString. Questo metodo è molto potente ed estremamente utile per implementare codice apposito per il controllo di tipo.

Quando Object.prototype.toString viene invocato utilizzando call() o apply(), questo restituisce il tipo dell'oggetto nel formato: [object Tipo]. La parte Tipo nel valore restituito costituisce il tipo di dato effettivamente ricercato.

Diamo un'occhiata a come funziona con qualche esempio:

// restituisce '[object Array]'
Object.prototype.toString.call([]); 

// restituisce '[object Date]'
Object.prototype.toString.call(new Date()); 

// restituisce '[object String]'
Object.prototype.toString.call(new String('freeCodeCamp'));

// restituisce '[object Boolean]'
Object.prototype.toString.call(new Boolean(true));

// restituisce '[object Null]'
Object.prototype.toString.call(null);

Questo significa che se prendiamo la stringa restituita ed estraiamo la parte Tipo, avremo il tipo di dato effettivo. Qui riporto un tentativo di implementare quanto appena detto:

function typeCheck(value) {
  const return_value = Object.prototype.toString.call(value);
  // possiamo usare anche le espressioni regolari per fare questo...
  const type = return_value.substring(
           return_value.indexOf(" ") + 1, 
           return_value.indexOf("]"));

  return type.toLowerCase();
}

Ora possiamo usare la funzione typeCheck per rilevare i tipi di dato:

typeCheck([]); // 'array'
typeCheck(new Date()); // 'date'
typeCheck(new String('freeCodeCamp')); // 'string'
typeCheck(new Boolean(true)); // 'boolean'
typeCheck(null); // 'null'

Riassumendo

Per riassumere quanto abbiamo imparato in questo articolo:

  • In JavaScript il controllo del tipo dei dati non è così rigoroso come in altri linguaggi di programmazione.
  • Utilizzo dell'operatore  typeof per rilevare i tipi di dato.
  • Ci sono due varianti della sintassi per l'operatore typeof: typeof e typeof(espressione).
  • Il risultato dell'operatore typeof può essere fuorviante a volte. In questi casi dobbiamo necessariamente affidarci ad altri metodi disponibili (Number.isNaN,  Array.isArray, e così via).
  • Possiamo usare Object.prototype.toString per creare un metodo generico per il controllo del tipo di dato.

Prima di terminare...

Grazie per aver letto questo articolo! Connettiamoci! Puoi taggarmi nei commenti su Twitter (@tapasadhikary).

Potrebbero interessarti anche questi articoli:

È tutto per ora. Ci rivediamo con il mio prossimo articolo. A presto!