Replacing let with const

Even when it feels impossible

In JavaScript, const affords a few benefits:

  1. It lets the team reviewing your code that the value is not going to change, improving confidence.
  2. It throws an error if you attempt to change that variable, preventing accidental re-assignment.
  3. It allows the engine to optimize for unchanging values.
  4. It avoids side effects caused by conflicting changes.
  5. It is more consistent with functional programming and immutable states.
  6. There are instances of better TypeScript inference.

It’s common practice to use const by default, denoting that a variable is constant and unchanging.

However, it can be confusing when you want a constant variable that is conditionally defined, but otherwise unchanging. As an example,

let height = 60;
if (name === 'Charles') {
height = 70;
} else if (
gender === Gender.Male &&
race === Race.White
) {
height = 69;
} else if (gender === Gender.Female) {
height = 64;
}

In the above example, I have conditionally defined the height variable. It starts as let so that I may reassign it immediately to what I want it to constantly be. It’s value will be unchanging going forward, and I really wish I were able to define it as a constant to represent how I do not want future code to ever change it.

There is always more than one way to solve a problem. In this case, you could use a really long ternary to define height as a const, but in some cases that might not be the best (or possible) solution. I want to propose a solution that is applicable to all uses of let, not just this one.

This solution may not be the most optimal for every use case, but it is applicable to every use case, and it is extensible to any refactor. If you ever find yourself using let, you can use this pattern to replace your let with const.

The answer is functions.

function getHeight({ gender, name, race }) {
if (name === 'Charles') {
return 70;
}
if (
gender === Gender.Male &&
race === Race.White
) {
return 69;
}
if (gender === Gender.Female) {
return 64;
}
return 60;
}
const height = getHeight({ gender, name, race });

In this example, each conditional assignment (name === 'Value') is replaced with a return statement. The use of return statements allows multiple to exist in one function scope, without the use of temporary variables (let).

I use this pattern all the time, and I love it.

Is the code longer? Yes. Is it more readable? Yes. It is more maintainable? Yes. Is it less subject to user error? Yes. Is it easier to unit test? Yes.

expect(getHeight({ name: 'Charles' })).toBe(70);
expect(getHeight({
gender: Gender.Male,
race: Race.White,
})).toBe(69);
expect(getHeight({ gender: Gender.Female })).toBe(64);
expect(getHeight({ gender: Gender.Male })).toBe(60);

If you ever find yourself using let so that you can assign based on subsequent conditions (if (...) { myVar = ...; }), consider using a function to execute those conditions and return that assignment (function abc() { if (...) { return ...; } }).

Happy coding!

Conclusion 🔚

If you have any questions, feedback, or relevant great advice, please leave it in the comments below.

To read more of my columns, you may follow me on LinkedIn and Twitter, or check out my portfolio on CharlesStover.com.

Senior front end engineer / charlesstover.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store