JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.
In this article, we’ll look at some tooling and testing best practices for writing robust JavaScript code.
Use TypeScript
TypeScript is a language that’s an extension of JavaScript to provide flexible and optional type checking for JavaScript code.
It builds code into JavaScript so that it can be run on the browser or in the Node.js environment.
The code is checked for type errors before the code is built into the final artifact. This means that data type errors, which would be runtime errors if it weren’t checked by TypeScript are prevented.
This is a big class of errors that are eliminated. The type checking isn’t rigid. It provides many ways to annotate the types of objects and return types of functions.
For instance, we can create interfaces to annotate the types of objects as follows:
interface Pet {
name: string;
walk(): void;
}
In the code above, we created an interface to indicate that whatever has the type Pet
must have the name
string property and a walk
method that returns nothing (hence the void
return type).
Then we can use that as follows:
const dog: Pet = {
name: 'joe',
walk() {
console.log('walk');
}
}
In the code above, we have the name
string property and the walk
method in the dog
object, which meets the requirement of the interface.
Therefore, the code will build and run by the TypeScript compiler. We’ll get an error if the types of anything are different from the interface.
Another good feature of TypeScript is the flexibility of the type checking. The types don’t have to be fixed, they can be flexible and dynamic as well.
For instance, we can create nullable types as follows:
interface Pet {
name: string;
walk?(): void;
}
In the code above, the walk
method has been made optional with the ?
after the method name. Now we can omit it in any object that has type Pet
.
We can also create union types of multiple types as follows:
interface Pet {
name: string;
walk(): void;
}
interface Animal {
breed: string;
}
const dog: Pet | Animal = {
name: 'joe',
breed: 'labrador',
walk(){}
}
In the code above, we have the Pet | Animal
type, which consists of the members of the Pet
and Animal
types combined together.
Therefore, our dog
object can have any member from each interface included in it.
Also, we don’t have to specify the types of an object directly. If we already have an object in our code and we want another object to have the same data type as that one, we can use the typeof
operator as follows to specify the type:
const cat = {
name: 'james'
}
const dog: typeof cat = {
name: 'joe'
}
In the code above, we specified the data type of the dog
object to be the same as the data type for cat
with the typeof dog
expression. This way, we don’t have to specify any types directly.
TypeScript has many more features that can help us write more solid JavaScript code by preventing data type errors that are otherwise caught at runtime.
This is especially important with big apps where there’s more chance of these errors happening.
Therefore, we should consider using TypeScript for writing more robust JavaScript code.
Photo by Jerry Wang on Unsplash
Write Tests
We should write automated tests to check for regressions in our code so that we can have peace of mind when changing code and all the automated tests pass.
It’s easy to write automated tests with the Jest test runner, which has many features to let us write and run tests for both front and back end JavaScript apps.
For instance, we can write a simple function with unit test as follows:
math.js
:
const multiply = (a, b) => {
return a * b;
}
module.exports = {
multiply
};
math.test.js
:
const math = require('./math');
test('multiplies 1 * 2 to equal 2', () => {
expect(math.multiply(1, 2)).toBe(2);
});
In the code above, the math.js
module has the function multiply
that we want to test.
Then in math.test.js
, we added our test for testing the multiply
function exported by math.js
.
The file names are the convention for Jest tests, where we have the name of the production code file corresponds with the name of the test file.
Then when we run Jest, this test will run and should pass because 1 times 2 is 2.
If we write unit tests for every part of our app then it’s easy to know whether our app is still working properly.
Conclusion
TypeScript is probably the best type checker for JavaScript. It’s a natural extension of JavaScript to add type annotations and checking to our JavaScript code.
Writing automated tests is always a good idea so that we can check that our code is working properly after we make changes to it.