Categories
JavaScript Best Practices

JavaScript Antipatterns — Formatting and More

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we format JavaScript code, naming, and white spaces.

Coding Conventions

We got to establish some coding conventions so that it’s predictable and neat.

That helps with understanding a lot better.

Indentation

It’s impossible to read code without indentation. Therefore, we should indent our code.

To do that, we should indent code inside blocks.

So we write:

const foo = () => {  
  console.log('foo')  
}

or:

for (let i = 0; i < max; i++) {  
  console.log('foo')  
}

or:

if (val === 'foo') {  
  console.log('foo')  
}

We do the same with any kind of nesting.

2 spaces is a good idea. It’s more portable than tabs as they’re the same everywhere.

A space character is always a space character, but tabs are up for interpretation.

Curly Braces

As we can see, we have curly braces in all our blocks.

We can skip them if we only have one line in the if or loop body.

But it’s not a good idea to skip them since it makes reading the blocks much easier.

Therefore, we should always use curly braces to delimit blocks.

For instance, we shouldn’t write:

for (let i = 0; i < 10; i++)  
  console.log(i);

But instead, we write:

for (let i = 0; i < 10; i++) {  
  console.log(i);  
}

Similar instead of writing the following if block:

if (i === 1)   
  console.log(i);

We write:

if (i === 1) {  
  console.log(i);  
}

Opening Brace Location

The opening brace is location is important. We may interpret the code differently in different places.

In JavaScript, it’s usually in the first line of the block.

For instance, we have:

if (i === 1) {  
  console.log(i);  
}

and:

for (let i = 0; i < 10; i++) {  
  console.log(i);  
}

and:

const foo = () => {  
  console.log('foo')  
}

where the opening brace is always on the first line.

This is especially important is we’re returning something.

For instance, the following:

const foo = () => {  
  return {  
    foo: 'foo'  
  }  
}

is different from:

const foo = () => {  
  return   
  {  
    foo: 'foo'  
  }  
}

The code above is actually the same as:

const foo = () => {  
  return;  
  {  
    foo: 'foo'  
  }  
}

So it won’t return the object.

Javascript automatically inserts semicolons when it thinks it makes sense.

Therefore, in return statements, we should have the opening curly brace in the same line as the return keyword.

White Space

Having white space also improves readability.

For instance, if we have:

x<1

That’s not too readable.

However, if we have:

x < 1

That’s a lot clearer.

Good places to put white spaces include the following locations:

  • after the semicolons for separating parts of a loop — for (let i = 0; i < 10; i++){ }
  • initializing multiple variables — let a = 1, b = 2;
  • after the comma that separate array item s— [1, 2, 3]
  • after commas in object properties — {a: 1, b: 2}
  • separating function arguments — fn(a, b, c)
  • before curly braces in functions declarations — function fn() {}
  • after the function keyword — function () {};

We should also put spaces between operands in arithmetic or comparison operations.

For instance, 1 + 2 is better than 1+2 ;

Naming Conventions

Names are also important. They’ve to descriptive and they have to be consistent.

This way we can predict what things mean and won’t have to look as hard to find out their true meaning.

Capitalizing Constructors and Classes

Constructor and class names should start with a capital.

For instance, we write:

function FooConstructor() {...}

and:

class Foo {...}

All words should start with capital as we can see.

Conclusion

We should stick to some common conventions for formatting JavaScript code.

This includes capitalizing first letters of words in constructor and class names.

Also, we should add spaces and indentation to make reading code easier.

In addition, we should always have curly braces for blocks whether they’re required or not.

Also, we should have braces in the right location so that they won’t be interpreted in ways that we don’t expect.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Prototypes, Switch, and More

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript prototypes, switch statements, use of eval and parsing integers.

Built-in Prototypes

We should never mess with prototypes of built-in constructors like Array or Function .

If we need extra functionality that they don’t provide, we either make our own class or function.

Messing with built-in prototypes will bring problems with conflicts and people will be confused about why something is added to the prototype or why some methods act differently than from the docs.

Properties that we add to the prototype might also show up in loops that don’t use hasOwnProperty , which is even more confusing.

For instance, we should never write something like:

Array.prototype.foo = function(){
  //...
}

Instead, we write our own function to pass in an array:

function(arr) {
  //...
}

switch Pattern

We can use switch statements to replace a series of if and else statements.

For instance, we can write:

let val = 0,
  result = '';

switch (val) {
  case 0:
    result = "zero";
    break;
  case 1:
    result = "one";
    break;
  default:
    result = "unknown";
}

instead of:

let val = 0,
  result = '';

if (val === 0) {
  result = 'zero';
} else if (val === 1) {
  result = 'one';
} else {
  result = 'unknown';
}

It’s longer but more readable.

We’ve to remember to add break to each case statement so that once a matching case is found, it’ll stop running the other cases.

Also, we should end with a default case so that we do something even if no matching value is found.

Avoiding Implied Typecasting

To avoid implied typecasting, we should avoid using the == operator for equality comparison.

The rules are complex and confusing for type conversions.

Instead, we should use the === operator for comparison:

const foo = 0;
if (foo === false) {
  // not running because foo is 0, not false
}

The type of both operands have to be the same before comparison, so the body won’t run.

Likewise, we use the !== operator for inequality comparison.

So we write:

const foo = 0;
if (foo !== false) {
  // runs because foo is 0, not false
}

If we use the < , > , <= , or >= operators, then we’ve to convert the types of the operators to the same types explicitly to avoid confusion.

Avoiding eval()

eval should be avoided. Security issues arise from running code from strings.

It’s also very hard to debug code that is in strings.

Also, browsers can’t optimize code that is in strings.

Therefore, we should never use it.

setInterval , setTimeout , and Function constructor all take strings.

So we shouldn’t use the Function constructor to define functions.

If we call setInterval or setTimeout , the callback should be a function and not a string.

So instead of writing:

setTimeout(`console.log('hello')`)

We write:

setTimeout(() => console.log('hello'))

Number Conversions with parseInt()

We can call parseInt to get a numeric value from a string.

It takes a 2nd radix parameter to specify the base of the number.

We should include the 2nd argument so we make sure that the number string is converted properly to a number.

For instance, we should write:

const num = parseInt("16", 10);

so that we know that 16 is a decimal number.

However, we should be careful with parseInt since it’ll try to convert strings with numbers and letters.

For instance, parseInt(“1 abc”) returns 1.

We can also use the + operator to convert something to a number by writing:

const num = +"16";

On the other hand, +”1 abc” return NaN , which is probably what we want.

Conclusion

We should never add methods to built-in prototypes.

It just confuses people and it may overwrite existing methods, which is no good.

switch statements may be good alternatives to if and else.

We shouldn’t use eval or related methods like the Function constructor or passing strings to setTimeout or setInterval .

Categories
React

React Antipatterns to Avoid

React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps.

In this article, we’ll look at some React antipatterns that we should avoid.

bind() and Functions in Components

When we call bind in React class components, we shouldn’t call them repeatedly in our props. Instead, we should call bind in the constructor so that we don’t have to call bind on the fly when we pass in our class component methods as props.

For instance, we can write the following code to do that:

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ""
    };
    this.updateValue = this.updateValue.bind(this);
  }
  updateValue(e) {
    this.setState({
      name: e.target.value
    });
  }
  render() {
    return (
      <>
        <form>
          <input onChange={this.updateValue} value={this.state.name} />
        </form>
        <p>{this.state.name}</p>
      </>
    );
  }
}

In the code above, we have an input which calls the updateValue method when the input value is changed.

We have:

this.updateValue = this.updateValue.bind(this);

in the constructor so that it’ll always bind the component as the value of this .

Now we don’t have to do it again repeatedly everywhere. Also, we won’t be creating a new function every time the onChange event is emitted since we didn’t call bind , which returns a new function.

The performance hit from creating methods on the fly when our app grows large is going to be noticeable in some situations.

So instead of writing:

<input onChange={this.updateValue.bind(this)} value={this.state.name} />

we write what we have above.

Missing key Prop or Using indexes in key Prop

The key prop is important when we render lists, even though it may seem like it’s doing nothing sometimes. The unique key prop value lets React identify list items correctly.

key prop values are used to match the children in the original tree with the children in the subsequent tree.

Therefore, using the array’s index is also bad because it may lead React to render the wrong data.

It’s best to use unique IDs as the value of the key prop. We can create an array with unique IDs that we can use as keys as follows:

import React from "react";
import { v4 as uuidv4 } from "uuid";

const arr = [
  {
    fruit: "apple",
    id: uuidv4()
  },
  {
    fruit: "orange",
    id: uuidv4()
  },
  {
    fruit: "grape",
    id: uuidv4()
  }
];

export default function App() {
  return (
    <div className="App">
      {arr.map(a => (
        <p key={arr.id}>{a.fruit}</p>
      ))}
    </div>
  );
}

In the code above, we used the uuid package to create a unique UUID to use as the value of the key prop. This way, there’s almost no chance of collision of unique IDs.

This way, IDs are unique and predictable, and we don’t have to think of a way to generate the unique ID for each item ourselves.

Components Name

Component names have to begin with a capital letter. For instance, if we write the following code, we’ll get an error from React:

import React from "react";

const foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <foo />
    </div>
  );
}

If we run the code above, React will give us the error ‘<foo> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.’

Therefore, we should instead name our components always with a capital letter to start:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo />
    </div>
  );
}

Then we’ll see ‘foo’ displayed on the screen as we expected.

Photo by chatnarin pramnapan on Unsplash

Close Every Element

All component tags have to have a closing tag or a slash in the end for self-closing components. For instance, if we have:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo>
    </div>
  );
}

We’ll get a syntax error because we’re missing a closing slash or tag in:

<Foo>

To make the app run, we write instead:

import React from "react";

const Foo = () => <p>foo</p>;

export default function App() {
  return (
    <div className="App">
      <Foo />
    </div>
  );
}

<Foo></Foo> also works.

Conclusion

It’s easy to make mistakes when we’re writing React components. We have to remember to capitalize the first letter of component names. Also, we have to close the tags of the component or add a closing slash for self-closing components.

Also, we should call bind first so that we don’t have to call it repeatedly to bind to the right value of this in class-based components.

Categories
JavaScript Best Practices

List of JavaScript Antipatterns

JavaScript is a language that’s used a lot in many places including web development and more.

Like any other language, it’s easy to commit antipatterns when we’re programming with JavaScript.

In this article, we’ll look at some antipatterns we should avoid.

Polluting the Global Namespace

Polluting the global namespace is bad because it’ll lead to name collisions,

Without strict mode, it’s also easy to accidentally declare global variables.

For instance, without strict mode, we can write something like:

x = 1;

which is the same as:

window.x = 1;

window is the global object in browser JavaScript.

Global variables are accessible everywhere so that we can change their values anywhere.

This isn’t good because we don’t want to run into bugs because of accidental reassignments.

An example of accidental assignment would be something like:

function foo() {
  return 1;
}

function bar() {
  for (i = 0; i < 10; i++) {}
}

i = foo();
bar();

where we assign i in many places. i was assigned in multiple places in a confusing way.

Extending the Object prototype

We should never add any new properties to Object.prototype .

This is because it’s global. It’ll affect other objects since almost all objects extend the Object.prototype .

If everyone does this, it’s very easy for people to overwrite the Object ‘s prototype with their own code.

Instead, we should use Object.defineProperty to define properties with property descriptors that we want to set if we want control over our object’s properties.

Using Multiple var Declarations Instead of One

Multiple var declarations are less readable and slightly slower, so instead of writing:

var a = 1;
var b = 2;
var c = 3;

we should write:

var a = 1,
  b = 2,
  c = 3;

Using new Array(), new Object(), new String(), and new Number()

There’s no reason to use the Array constructor. It’s confusing since the single argument versions and the multiple arguments version do different things.

The single-argument version takes a number and returns an array with the length set by the argument.

The multiple argument version takes the content of the array as arguments. We can pass in as many as we want to populate our new array.

For instance:

const arr = new Array(5);

returns an array with 5 empty slots.

On the other hand,

const arr = new Array(1, 2, 3);

returns [1, 2, 3] .

Instead, we should use array literals to make things simple for us:

const arr = `[1, 2, 3];`

It’s simpler and less confusing.

Likewise, new Object() is just extra writing. We have to define the properties on different lines as follows if we want to add properties:

const obj = new Object();
obj.a = 1;
obj.b = 2;
obj.c = 3;

Then obj ‘s value is {a: 1, b: 2, c: 3} .

That’s just extra writing. Instead, we should use object literals instead:

const obj = {
  a: 1,
  b: 2,
  c: 3
}

It’s much less typing and also pretty clear.

The problem with new String() and new Number() are that they give us the type 'object' . There’s no reason that we need strings and numbers to have the type 'object' .

We’ve to call valueOf() if we want to convert them back to primitive values.

Instead, we should use the factory functions String() and Number() to convert things to string or number.

For instance, instead of writing:

const str = new String(1);

We write:

const str = String(1);

Also, instead of writing:

const num = new Number('1');

We write:

const num = Number('1');

Relying on the Ordering of Iterator Functions Like .map and .filter

We shouldn’t rely on the iteration order of forEach, map and filter .

The iteration also doesn’t work if we pass in async function as the filter.

If we need to iterate through things in a specific order, then we should sort them the way we want before iteration.

Also, if we want to use the index of the entry, we can pass it into the callback so that we get index reliably.

For instance, we can write the following to get the index and the original array in the callback as follows:

const nums = [1, 2, 3];
nums.forEach((num, index, nums) => {
  if (index === nums.length - 1) {
    console.log('end');
  }
})

This is much better than:

const nums = [1, 2, 3];
let count = 0;
nums.forEach((num) => {
  if (count === nums.length - 1) {
    console.log('end');
  }
  count++
})

Not only the second example more complex, but it’s less reliable since we may accidentally change the count elsewhere.

On the other hand, accessing the index and original array from the callback parameters means there’s no chance of modifying count outside.

This also applies to map and filter .

Conclusion

We can should avoid antipatterns like global variables, unnecessary use of constructors, and extending the Object ‘s prototype.

Also, since we can access the numbers from the parameters, we shouldn’t create variables to track the index on the outside.

Categories
TypeScript Best Practices

TypeScript Antipatterns to Avoid

TypeScript is a language that extends the capabilities of JavaScript by adding type annotations to JavaScript code. This lets us avoid bugs from unexpected data types.

In this article, we’ll look at some antipatterns to avoid when writing TypeScript code.

Overusing the any Type

The point of using TypeScript is to have types in variables and functions. So we should use them wherever we can.

Therefore, we shouldn’t use the any type in most of our code.

Overusing Classes

If our TypeScript classes don’t have many methods, then we don’t need to define a class just to use it to type variables and functions.

Also, if we only have a single instance, then wrapping the logic within a class doesn’t make sense.

Instantiating classes introduce complex and it’s hard to optimize when minifying code.

Instead, we can define object literals and use interfaces or the typeof operator to get the type of an object.

For example, write the following interface for objects:

interface Person {
    firstName: string;
    lastName: string;
}

const person: Person = {
    firstName: 'Jane',
    lastName: 'Smith'
}

Then we can use them like we have above with person .

Also, if we don’t know the exact structure that the object will have, we can use typeof operator as follows:

const person = {
    firstName: 'Jane',
    lastName: 'Smith'
}

const person2: typeof person = {
    firstName: 'Joe',
    lastName: 'Smith'
}

Using typeof is a convenient way to define types without writing lots of code.

If our object has methods, we can do the same thing as above:

interface Person {
    firstName: string;
    lastName: string;
    fullName: (firstName: string, lastName: string) => string
}

const person: Person = {
    firstName: 'Jane',
    lastName: 'Smith',
    fullName(firstName: string, lastName: string) {
        return `${firstName} ${lastName}`;
    }
}

TypeScript will do type inference with typeof :

const person = {
    firstName: 'Jane',
    lastName: 'Smith',
    fullName(firstName: string, lastName: string) {
        return `${firstName} ${lastName}`;
    }
}
const person2: typeof person = {
    firstName: 'Joe',
    lastName: 'Smith',
    fullName(firstName, lastName) {
        return `${firstName} ${lastName}`;
    }
}

It’ll force us to add the fullName method to person2 if it’s missing, and it’ll force us to add the parameters and return a string.

Otherwise, we’ll get compiler errors.

Using the Function Type

The Function type is a generic type for functions. It’s like any for variables. We should specify the data types of parameters and return types in our functions.

Instead, we should add types to parameters and return types as follows:

type ArithmeticFn = (a: number, b: number) => number
const add: ArithmeticFn = (a: number, b: number): number => a + b;

We have the types in the type alias ArithmeticFn and also add . That’s enough type annotations in our code.

Also, once again, we can use typeof to do type inference:

const add = (a: number, b: number): number => a + b;
const subtract: typeof add = (a, b) => a-b

Then subtract also has the same parameter and return types as add .

Messing with Type Inference

We shouldn’t put useless type annotations with type inference will do the job.

For example, in the following example:

const courses = [{
    name: 'Intro to TypeScript'
}]
const [course] = courses;
const newCourse: any = {...course};
newCourse.description = 'Great intro to TypeScript';
courses[0] = newCourse;

We have an extra any annotation that we shouldn’t have.

Instead, if we want to add a new property to an object after copying it, we can write the following:

const courses = [{
    name: 'Intro to TypeScript'
}]
const [course] = courses;
const newCourse = {...course, description: 'Great intro to TypeScript'};
courses[0] = newCourse;

Then the TypeScript compiler won’t throw an error and we still get type inference from the TypeScript compiler since we didn’t use the any type.

Copying and Pasting Partial Type Definitions

Another thing that we shouldn’t do is copy and pasting partial type definitions from other places into our own code.

If we want to get the type of an object with an unknown type, we can use the typeof operator.

For example, we can write:

const person = {
    firstName: 'Joe',
    lastName: 'Smith',
    age: 20
}

const person2: typeof person = {
    firstName: 'Jane',
    lastName: 'Smith',
    age: 20
}

The TypeScript compiler will automatically recognize the type of person and will do checks when we define person2 to check if everything’s there.

Lookup Type

We can also use one property of an object as its own type. This is called a lookup type.

For example, we can write:

const person = {
    firstName: 'Joe',
    lastName: 'Smith',
    age: 20
}

const foo: typeof person.firstName = 'foo';

In the code above, TypeScript recognized person.firstName ‘s type is a string, so typeof person.firstName would be a string.

Mapped Types

We can create mapped types to map all properties of a type to something else.

For example, we can make all the properties of a type optional by writing:

interface Person{
    firstName: string;
    lastName: string;
}

type Optional<T> = {
    [P in keyof T]?: T[P];
};

const partialPerson: Optional<Person> = {};

The code above compiles and runs because we created a new type from Person where all the properties are optional.

Getting the Return Type of a Function

We can get the return type function with the ReturnType generic to pass in a type of the function. Then we’ll get the return type of that function.

For example, if we have:

const add = (a: number, b: number) => a + b;
const num: ReturnType<typeof add> = 1;

Then ReturnType<typeof add> will be number , so we have to assign a number to it.

TypeScript’s type inference is also working here. The ReturnType generic is available since TypeScript 2.8

Conclusion

In many cases, we can use the typeof operator, lookup types, or mapped types to annotate the types flexibly without losing type check capabilities.

This means we should eliminate the use of any as much as possible.

Also, we don’t need classes just for adding types to a few objects.