Skip to content

Instantly share code, notes, and snippets.

@Restuta
Last active September 24, 2020 01:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Restuta/d39f81421887da265c782a681fa932ad to your computer and use it in GitHub Desktop.
Save Restuta/d39f81421887da265c782a681fa932ad to your computer and use it in GitHub Desktop.
null or undefined

null in javascript is an object, which has few important, arguably confusing implications. Unfortunately JS has two ways to declare that something is "nothing", and the default way to do so is undefined.

Having two ways to declare that something is "nothing" might be confusing and could be unnecessary. I can't come up with a use-case where I'd need two (and Douglas C. thinks the same), therefore we should pick one or another.

Below is why I think we should pick undefined and not null:

ES5 world

We have plenty of code like this:

// pretty common pattern to define default values
function foo(params) {
  const x = params.x || 'default value'
  console.log(x)
}

foo({x: null}) // default value
foo({x: undefined}) //default value
foo({}) //default value

ES6+ world

Now you bravely moving to ES6 and love destructuring so you will happily refactor above to:

// pretty common pattern to define default values
function foo({x = 'default value'}) {
  console.log(x)
}

Now can you guess what would be the output? If you can, awesome, I couldn't and when I explained it to a few friends, they were confused as well. It's somewhat non-intuitive:

foo({x: null}) // null
foo({x: undefined}) //default value
foo({}) //default value

it's even less intuitive if you would want to guard all your params against null and undefined

// ES5
function foo(params) {
  let myParams = params || {}
  const x = myParams.x || 'default value'
  console.log(x)
}

foo() // default value
foo(null) //default value
foo(undefined) //default value

//ES6+
function foo({x = 'default value'}) {
  console.log(x)
}

foo() // exception is thrown
foo(null) //exception is thrown
foo(undefined) //exception is thrown

//Uncaught TypeError: Cannot destructure property `x` of 'undefined' or 'null'.

so you can do this:

function foo({x = 'default value'} = {}) {
  console.log(x)
}

foo() // "default value"
foo(null) //exception is thrown
foo(undefined) //"default value"

With last example our code is no longer safe, we need to additionally guard against null or there is a chance to get a runtime exception. At the same time we have another "nothing" in the language that doesn't behave like an object and it would be "safe" to use it in this case.

I feel like this alone is a very strong 💪 argument against null and for enabling corresponding eslint rule.


Few arguments from authority: TypeScript advices against null https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined More details: https://basarat.gitbook.io/typescript/recap/null-undefined

Douglas Crockford also against null https://www.youtube.com/watch?v=PSGEjv3Tqo0&feature=youtu.be&t=9m21s


Lodash returns undefined and not null, e.g. _.find and any other places where you would expect "null value".

@joebalancio
Copy link

Another observation is that properties on an object that are stringified as JSON are removed from the serialized string if the value of the property is undefined. The value undefined is not part of the spec. http://www.json.org/

> JSON.stringify({a: null, b: undefined })
'{"a":null}'

@Restuta
Copy link
Author

Restuta commented Oct 18, 2017

@joebalancio yep, that's a good observation. Imo, it doesn't affect above arguments thought.

@joebalancio
Copy link

@Restuta Other thoughts that I think should be considered.

  • How do we interact with APIs and code-bases (even our legacy code) that use null?
  • If we decide with undefined as "nothing", should we limit the scope of it's use (see above)?
  • How does this affect API communication between client and server? The backend has provided null as a value to properties to communicate the shape of an object to the frontend. How do we do this when undefined is removed from serialized JSON?

@Restuta
Copy link
Author

Restuta commented Oct 18, 2017

@joebalancio

  • we pass null when API requires us to, nothing we can do about it
  • no, we should use it while it works, unless we have a reason not to. I think rule should be “use undefined unless you are absolutely have to use null”
  • i’d like to question that “shape of data” thing. It doesn’t seem to me like it’s a good idea to pass extra payloads to the client just to define a shape. There are better ways to define a shape like shared schemas, API docs, TypeScript etc. If the client asks for it specifically (isn’t GraphQL for instance) then sure, we would pass “null”, because that is what JSON is using.

I don’t see a problem with using null for cases when we have to, I do see a problem using it in our own code when undefined would work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment