Przejdź do treści

Jak sprawdzić typ zmiennej w JavaScript? Operator typeof

Język programowania JavaScript jest dynamicznie typowany, co oznacza, że zmienne mogą zmieniać typy danych podczas wykonywania programu. Mając to na uwadze, jak możemy określić typ danego obiektu w dowolnym momencie?

Tu z pomocą przychodzi operator typeof.

Baner promujący artykuł

Dzięki temu artykułowi dowiesz się:

  • Jak sprawdzić typ zmiennej w JavaScript?
  • Jak sprawdzić, czy zmienna jest tablicą?

Typowanie w języku JavaScript

Wbrew niektórym opiniom, w JavaScript istnieją typy. Sprawdzanie ich działa w runtime.

Więcej o samym “runtime” w artykule na tym blogu pt. “Co to jest runtime?”.

Zatem, czy istnieje mechanizm, który sprawdzi nam typy w fazie kompilacji?

Oczywiście, ale nie w JavaScript. Tak działa TypeScript a dokładnie TypeScript Compiler (tsc), który to jest uruchomiony do tego, aby:

  • w edytorach, aby podświetlać błędy w kodzie,
  • w terminalu, aby wygenerować plik(i) JavaScript, który(e) zostaną zinterpretowane przez przeglądarkę.

typeof w specyfikacji ECMA-262 edycja 14 (2023)

Formalny zapis prosto z oficjalnego dokumentu:

ECMA-262 14th edition — 13.5.3 The typeof Operator
ECMA-262 14th edition — B.3.6.3 Changes to the typeof Operator

Zasada działania (bez języka formalnego, tak zwyczajnie, po polsku)

Specyfikacja jest pisana… specyficznym językiem. Myślę, że dobrze jest potrafić ją czytać.

Swego czasu blog V8 opublikował całą serię dotyczącą czytania specyfikacji ES https://v8.dev/blog/tags/understanding-ecmascript

Natomiast, gdy ktoś nigdy nie widział powyższego zapisu to opiszę jak działa operator typeof potocznym językiem.

Przykład: typeof val

  1. Zmienna val przechowuje wynik wykonania wyrażania znajdującego się po prawej stronie operatora typeof
  2. Sprawdzamy, czy val należy do tzw. Reference Record, tj.
    • jeśli nie należy, to zwracamy undefined
  3. Pobieramy wartość zmiennej, bo np. val nie musi być zmienną, może być dowolnym wyrażeniem (np. odwołaniem do właściwości obiektu - object.prop, w takim przypadku wyliczamy jego wartość)
  4. Jeśli val to undefined zwracamy "undefined"
  5. Jeśli val to null zwracamy "object"
  6. Jeśli val to String zwracamy "string"
  7. Jeśli val to Symbol zwracamy "symbol"
  8. Jeśli val to Boolean zwracamy "boolean"
  9. Jeśli val to Number zwracamy "number"
  10. Jeśli val to BigInt zwracamy "bigint"
  11. Sprawdzamy, czy val jest obiektem
  12. ⚠️ UWAGA: Jeśli typem val jest Object oraz val jest równe document.all to zwracamy undefined
  13. Jeśli val jest możliwa do uruchomienia zwracamy "function"
  14. Zwracamy "object" dla wszystkich innych wartości

Reference Record

Czytając specyfikację, można się dowiedzieć, że jest to wyrażenie:

  • x - pobranie referencji
  • x[y] - dostęp do elementu tablicy na podstawie indeksu
  • x.a - dostęp do właściwości obiektu na podstawie nazwy
  • super.a - dostęp do właściwości przodka (używamy w klasach dziedziczących)

document.all

Za czasów świetności Internet Explorera (IE), który był liderem wśród przeglądarek internetowych we wczesnych latach dwutysięcznych, próbował rozwijać język samodzielnie dodając coś nowego do JavaScript (a nawet JScript).

Jednym z takim wyjątkowych elementów, jest właśnie kolekcja document.all. Która to NIESTETY musi być wspierana z uwagi na kompatybilność wsteczną, natomiast ta kolekcja jest bardzo specyficzna.

UWAGA:

typeof document.all === undefined // true
Boolean(document.all) === false   // true

Pomimo, że:

document.all.toString() === '[object HTMLAllCollection]'  // true
typeof document.all.length === 'number'                   // true

Dlatego też, w dzisiejszych czasach nie korzysta się z document.all, a jeśli istnieje potrzeba pobrania wszystkich elementów drzewa DOM, to można osiągnąć to za pomocą document.querySelectorAll('*').

Reserved Word

Operator typeof należy do kolekcji Reserved Words oznacza to, że nie można użyć tej nazwy do własnych identyfikatorów, tj. funkcji, zmiennych, itd.

let typeof = 2; // Uncaught SyntaxError: Unexpected token 'typeof'

Jest różnica między “słowami kluczowymi” a “słowami zastrzeżonymi”.
Dobrym przykładem jest wyraz async, który należy do słów kluczowych, ale nie do zastrzeżonych, tj. możemy go użyć jako identyfikatora zmiennej.

const async = 'yes';
console.log(async); // 'yes'

Więcej informacji na ten temat w specyfikacji:
https://tc39.es/ecma262/#sec-keywords-and-reserved-words

Przypadki użycia typeof

  • Q: A do czego możemy wykorzystać ten operator?
  • A: Do sprawdzenia, z jakim typem wartości mamy do czynienia.

Poniżej przykłady wszystkich sprawdzeń.

// undefined
typeof undefined === 'undefined'
// ⚠️ UWAGA: Ukryty undefined
typeof document.all === 'undefined'
// boolean
typeof true === 'boolean'
typeof false === 'boolean'
typeof Boolean() === 'boolean'
// number
typeof 1 === 'number'
typeof 0 === 'number'
typeof -1 === 'number'
typeof NaN === 'number'
typeof Number() === 'number'
// string
typeof "" === 'string'
typeof "text" === 'string'
typeof String() === 'string'
typeof Date() === 'string'
// function
typeof function () {} === "function"
typeof (() => {}) === "function"
typeof Function() === "function"
typeof class {} === "function"
typeof Date === "function"
typeof Object === "function"
// object
typeof {} === 'object'
typeof Object() === 'object'

// Array object
typeof [] === 'object'
typeof Array() === 'object'

// Promise object
typeof Promise.resolve() === 'object'
typeof Promise.reject() === 'object'

// RegExp object
typeof /e/i === 'object'
typeof new RegExp('e', 'i') === 'object'

// operator `new` zawsze tworzy object
typeof new Array() === 'object'
typeof new Boolean() === 'object'
typeof new Date() === 'object'
typeof new Object() === 'object'
typeof new Function() === 'object'
typeof new String() === 'object'

// kolekcja danych
typeof new Map() === 'object'
typeof new WeakMap() === 'object'
typeof new Set() === 'object'
typeof new WeakSet() === 'object'

// ⚠️ UWAGA: Ukryty obiekt
typeof null === 'object'
// symbol
typeof Symbol() === 'symbol'
typeof Symbol('text') === 'symbol'
typeof Symbol(111) === 'symbol'
typeof Symbol.for() === 'symbol'
typeof Symbol.for('text') === 'symbol'
// bigint
typeof BigInt(10e100) === 'bigint'

Wskazówka

Każdy operator w JavaScript możemy uruchomić z wartością otoczoną w nawiasy lub też bez nich. Zatem równoważny jest zapis:

typeof x === typeof (x)

Dobrą praktyką (a raczej standardem formatowania) jest nieużywanie nawiasów.

Co z tablicą?

Tablica jest obiektem. Operator typeof, niestety nie jest tutaj pomocny.
Aby sprawdzić, czy mamy do czynienia z tablicą, należy wykorzystać funkcję Array.isArray().

Array.isArray([]) === true
Array.isArray({}) === false

typeof [] === 'object'
typeof {} === 'object'

A teraz zobaczmy jak wygląda specyfikacja funkcji Array.isArray():

ECMA-262 14th edition — 23.1.2.2 Array.isArray ( arg )
ECMA-262 14th edition — 7.2.2 IsArray ( argument )

typeof w popularnych bibliotekach

Przykład wykorzystania operatora typeof w bibliotekach (oraz frameworkach):

Podsumowanie

Operator typeof określa typ wartości, jaką przechowuje zmienna. Bardzo przydatne rozwiązanie w trakcie weryfikacji, z jakimi danymi mamy do czynienia.