Use `const` and make your JavaScript code better

Learn why using const makes your code better and learn three quick tips to use it in your code to make it easier to reason about.

Don’t use var

First of all, don’t use var. There are a few differences between var, let and const and the most important one to me is that let and const remove the error-prone behavior with variable hoisting. See this example:

// ❌ Don't do this
function foo() {
console.log(bar) // No error, but prints undefined
var bar = 1234
}
// ✅ This is better
function foo() {
console.log(bar) // ReferenceError: bar is not defined
const bar = 1234
}

Having an error while developing is better than having buggy behavior in production, so I prefer the strictness of let and const. Besides, your linter should’ve warned you about the variable not being defined, so with let, const and the proper tooling you won’t have any of the previous problems.

There’s only one case where I still use var: Node.js’ REPL or the browser’s console. Why? Because I want to be able to reuse the same variable name many times. That’s a desirable behavior when you are experimenting with things, but not in production code!

Const != immutable

It’s very important to understand const. It doesn’t imply immutability.

A variable is like a pointer to a value (it’s a pointer for objects, it’s an assigned value for primitives). const prevents the variable to be assigned to another value. We could say it makes the pointer immutable, but it doesn’t make the value immutable too!

const arr = [1, 2, 3]
arr.push(4) // this is totally fine
arr = ['foo', 'bar'] // TypeError: Assignment to constant variable.

So beware that arrays and objects assigned to const variables can be mutated. However numbers, booleans and strings are immutable per se, so they cannot be mutated. Not because you are using const but just because they are intrinsically immutable.

Why const is better

But why const is better? In my opinion it makes the code easier to reason about, basically because since it cannot be reassigned you have less scenarios, side effects or edge cases to reason about. One super simple example:

const somethingConst = 'some default value'
// A lot of code here...
console.log(somethingConst)
// You know what the code above will print
// without going through all the previous code.
// You just know it, since the variable cannot be changed

Use a default value with the || operator

In many scenarios the value assigned to a variable will depend on some conditions. For example it is very frequent to assign the returned value of a function to it, but maybe the function returns null or undefined and in that case you want to use a default value. In that case you will probably be tempted to use an if but then you cannot use const since you will reassign the variable value if the condition is met, and we cannot do that, and we don’t want to do that with const. But, in JavaScript there’s a nice solution to that.

// ❌ Don't do this
let foo = something()
if (!foo) {
foo = defaultValue
}
// ✅ This is better
const foo = something() || defaultValue

Beware that defaultValue will be used for any falsy value returned by something().

Use the ternary operator ? :

Sometimes you want to assign one value or another depending on a condition that is neither the first value nor the second one. You will be tempted to write an if again in this case. But you can make use of the ternary operator instead!

// ❌ Don't do this
let foo
if (condition) {
foo = valueA
} else {
foo = valueB
}
// ✅ This is better
const foo = condition ? valueA : valueB

If the sentence is long enough I will split it into different lines:

const foo = conditionLongEnoughToSplitTheSentence
? valueA
: valueB

Exctract functions

But if there are multiple conditions or they are really complex or long enough, it can be a good idea to extract a function instead.

// ❌ Don't do this
let foo
if (conditionA) {
if (conditionB && !conditionC) {
foo = value1
} else {
foo = value2
}
foo = value3
} else {
foo = value4
}
// ✅ This is better
const calculateFoo = () => {
if (conditionA) {
if (conditionB && !conditionC) {
return value1
} else {
return value2
}
return value3
}
return value4
}
const foo = calculateFoo()

Extra ball: TypeScript

Another benefit of using const is that the TypeScript compiler will calculate the types for you.

// ❌ Don't do this
let foo: number[] | string[]
if (condition) {
foo = [1, 2, 3]
} else {
foo = ['a', 'b', 'c']
}
// ✅ This is better
const foo = condition ? [1, 2, 3] : ['a', 'b', 'c']

In the first example you have to add the typing information; otherwise the any type is assigned to foo, and that’s something you want to avoid. In the second example the compiler is able to deduct the types so you don’t have to write them!

Nevertheless sometimes you might want to add the typing information yourself to enforce the typing and prevent the future you (or a teammate) change the expression and thus change the variable type.

// This enforces the typing, so you prevent somebody
// changing the type of foo indirectly by changing the
// type of either `value1` or `value2`
const foo: number[] = condition ? value1 : value2

I only enforce the typing this way when there’s something that can be error-prone or is confusing.


To recap:

  • Don’t use var
  • Use the || or ternary operators to initialize const variables that need to be calculated based on some conditions.
  • Extract functions if the conditions are long or complex enogh.
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.