惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
D
DataBreaches.Net
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
B
Blog
U
Unit 42
有赞技术团队
有赞技术团队
博客园 - 聂微东
GbyAI
GbyAI
宝玉的分享
宝玉的分享
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
Jina AI
Jina AI
Martin Fowler
Martin Fowler
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
P
Proofpoint News Feed
A
About on SuperTechFans
I
InfoQ
博客园 - 【当耐特】
C
Check Point Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
Y
Y Combinator Blog
Project Zero
Project Zero
WordPress大学
WordPress大学
小众软件
小众软件
AWS News Blog
AWS News Blog
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
I
Intezer
Engineering at Meta
Engineering at Meta
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
Type Checking in JavaScript is Slightly Screwed
Dmitri Pavlutin · 2019-12-03 · via Dmitri Pavlutin Blog

JavaScript's dynamic typing is good and bad at the same time. It's good because you don't have to indicate the variable's type. It's bad because you can never be sure about the variable's type.

typeof operator determines the 6 types in JavaScript:


typeof 10; // => 'number'

typeof 'Hello'; // => 'string'

typeof false; // => 'boolean'

typeof { a: 1 }; // => 'object'

typeof undefined; // => 'undefined'

typeof Symbol(); // => 'symbol'


As well, instanceof checks the constructor of an instance:


class Cat { }

const myCat = new Cat();

myCat instanceof Cat; // => true


But some behavior of typeof and instanceof can be confusing. The edge cases are wiser to know in advance.

This post describes the pitfalls and remedial workarounds of using typeof and instanceof.

1. The type of null

typeof null in JavaScript meme

typeof myObject === 'object' would tell you if myObject is an object type. Let's try that in an example:


const person = { name: 'batman' };

typeof person; // => 'object'


typeof person is 'object' because person holds a plain JavaScript object.

Variables that hold objects, sometimes, could be empty. In such case you would need null value. Here are a few use-cases:

  • You can use null to skip indicating configuration objects
  • You can initialize with null the variables that later will hold objects.
  • When a function cannot construct an object for some reason, it can return null

For example, str.match(regExp) method returns null if no regular expression matches occur:


const message = 'Hello';

message.match(/Hi/); // => null


Can you use typeof to differentiate an existing object from a null missing object?

Unfortunately, you can't:


let myObject = null;

typeof myObject; // => 'object'

myObject = { prop: 'Value' };

typeof myObject; // => 'object'


typeof with an existing object and with null evaluates to 'object'.

"The history of typeof null" describes this bug in detail.

A good approach to detect if a variable has an object, and no null values, is this:


function isObject(value) {

return typeof value === 'object' && value !== null;

}

isObject({}); // => true

isObject(null); // => false


In addition to checking that value is an object: typeof value === 'object', you also explicitely verify for null: value !== null.

2. The type of an array

If you try to detect if a variable contains an array, the first temptation is to use typeof operator:


const colors = ['white', 'blue', 'red'];

typeof colors; // => 'object'


However, the type of the array is an 'object' too. While technically an array is an object, that's slightly confusing.

The correct way to detect an array is to use explicitely Array.isArray():


const colors = ['white', 'blue', 'red'];

const hero = { name: 'Batman' };

Array.isArray(colors); // => true

Array.isArray(hero); // => false


Array.isArray(colors) returns a boolean true, indicating that colors is an array.

3. Falsy as type check

undefined in JavaScript is a special value meaning uninitialized variable.

You can get an undefined value if you try to access an uninitialized variable, non-existing object property:


let city;

let hero = { name: 'Batman', villain: false };

city; // => undefined

hero.age; // => undefined


Accessing the uninitialized variable city and a non-existing property hero.age evaluates to undefined.

To check if a property exists, and undefined being falsy, you might have the tempration to use object[propName] in a condition:


function getProp(object, propName, def) {

// Bad

if (!object[propName]) {

return def;

}

return object[propName];

}

const hero = { name: 'Batman', villain: false };

getProp(hero, 'villain', true); // => true

hero.villain; // => false


object[propName] evaluates to undefined when propName doesn't exist in object. if (!object[propName]) { return def } guards for missing properties.

hero.villain property exists and is false. However, the function incorrectly returns true when accessing villan prop value: getProp(hero, 'villain', true).

undefined is a falsy value. As well as false, 0, '' and null.

Don't use falsy as a type check of undefined. Explicitly verify if the property exists in the object:

  • typeof object[propName] === 'undefined'
  • propName in object
  • object.hasOwnProperty(propName)

Let's improve getProp() function:


function getProp(object, propName, def) {

// Better

if (!(propName in object)) {

return def;

}

return object[propName];

}

const hero = { name: 'Batman', villain: false };

getProp(hero, 'villain', true); // => false

hero.villain; // => false


if (!(propName in object)) { ... } condition correctly determines if the property exists.

Logical operators

I think it's better to avoid using logical operator || as a default mechanism. My reading flow breaks when I see it:


const hero = { name: 'Batman', villain: false };

const name = hero.name || 'Unknown';

name; // => 'Batman'

hero.name; // => 'Batman'

// Bad

const villain = hero.villain || true;

villain; // => true

hero.villain; // => false


hero has a property villain with value false. However the expression hero.villain || true evaluates to true.

The logical operator || used as a default mechanism to access properties fails when the property exists and has a falsy value.

To default when the property does not exists, better options are the new nullish coalescing operator:


const hero = { name: 'Batman', villan: false };

// Good

const villain = hero.villain ?? true;

villain; // => false

hero.villain; // => false


Or destructuring assignment:


const hero = { name: 'Batman', villain: false };

// Good

const { villain = true } = hero;

villain; // => false

hero.villain; // => false


4. The type of NaN

Ultimate Answer to Universe: NaN

The integers, floats, special numerics like Infinity, NaN are of the type number.


typeof 10; // => 'number'

typeof 1.5; // => 'number'

typeof NaN; // => 'number'

typeof Infinity; // => 'number'


NaN is a special numeric value created when a number cannot be created. NaN is an abbreviation of not a number.

A number cannot be created in the following cases:


// A numeric value cannot be parsed

Number('oops'); // => NaN

// An invalid math operation

5 * undefined; // => NaN

Math.sqrt(-1); // => NaN

// NaN as an operand

NaN + 10; // => NaN


Because of NaN, meaning a failed operation on numbers, the check of numbers validity requires an additional step.

Let's make sure that isValidNumber() function guards against NaN too:


function isValidNumber(value) {

// Good

return typeof value === 'number' && !isNaN(value);

}

isValidNumber(Number('Z99')); // => false

isValidNumber(5 * undefined); // => false

isValidNumber(undefined); // => false

isValidNumber(Number('99')); // => true

isValidNumber(5 + 10); // => true


In addition to typeof value === 'number', it's wise to verify !isNaN(value) for NaN.

5. instanceof and the prototype chain

Every object in JavaScript references a special function: the constructor of the object.

object instanceof Constructor is the operator that checks the constructor of an object:


const object = {};

object instanceof Object; // => true

const array = [1, 2];

array instanceof Array; // => true

const promise = new Promise(resolve => resolve('OK'));

promise instanceof Promise; // => true


Now, let's define a parent class Pet and its child class Cat:


class Pet {

constructor(name) {

this.name;

}

}

class Cat extends Pet {

sound = 'Meow';

}

const myCat = new Cat('Scratchy');


Now let's try to determine the instance of myCat:


myCat instanceof Cat; // => true

myCat instanceof Pet; // => true

myCat instanceof Object; // => true


instanceof operator says that myCat is an instance of Cat, Pet and even Object.

instanceof operator searches for object's constructor through the entire prototype chain. To detect exactly the constructor that has created the object look at the constructor property of the instance:


myCat.constructor === Cat; // => true

myCat.constructor === Pet; // => false

myCat.constructor === Object; // => false


Only myCat.constructor === Cat evaluates to true, indicating exactly the constructor of the myCat instance.

6. Key takeaways

The operators typeof and instanceof perform the type checking in JavaScript. While they are generally simple to use, make sure to know the edge cases.

A bit unpexpected is that typeof null equals 'object'. To determine if a variable contains a non-null object, guard for null explicitely:


typeof myObject === 'object' && myObject !== null


The best way to check if the variable holds an array is to use Array.isArray(variable) built-in function.

Because undefined is falsy, you might be tempted to use it directly in conditionals. But such practice is error-prone. Better options are prop in object to verify the property existence, nullish coalescing object.prop ?? def or destructuring assignment { prop = def } = object to access potentially missing properties.

NaN is a special value of type number created by an invalid operation on numbers. To be sure that a variable has a "correct" number, it's wise to use a more detailed verification: !isNaN(number) && typeof number === 'number'.

Finally, remember that instanceof searches for the constructor of the instance through the prototype chain. Without knowing that, you could get a false-positive if you verify a child's class instance with the parent class.

What JavaScript type checking pitfalls do you know?