Destructuring for type safety in JavaScript
Tags: javascript, typesafety, programmingpatterns
Published: 2023-07-25 09:50:25

---
title: Destructuring for type safety in JavaScript
published: true
description: There's no straightforward way to check for types every argument of a JavaScript function. However, using destructuring on created classes can help achieve some kind of type safety.
tags: javascript, typesafety, programmingpatterns
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz4kckrgvnha4g8rabvp.jpg
---

JavaScript allows you to use classes for modularization, but it's not easy to make a function just work with a single type. Let's see an example, taken from [NodEO](https://github.com/JJ/nodeo), an [evolutionary algorithm](https://www.techtarget.com/whatis/definition/evolutionary-algorithm) library, now in a heavy refactorization phase.

```javascript
export class StringChromosome extends Chromosome {
  constructor(aString, fitness) {
    super(fitness);
    this.stringChr = aString;
  }
  // more stuff
}

export class FloatChromosome extends Chromosome {
  constructor(aVector, fitness = 0) {
    super(fitness);
    this.floatVector = aVector;
  }
  // more stuff here
}
```

A chromosome has a common fitness, that is, how well it solves the problem, plus a data structure that represents the problem; EAs don't tell you which data structure you should use, so different types of chromosomes will have different data structures; we will use different names for those attributes, reflecting what they actually are.

Chromosomes undergo *mutation*; they essentially change randomly, looking for a better solution. A first version of a mutation operator would look like this:

```javascript 
// defined within FloatChromosome
static mutationRange = 0.2;
tatic mutate(chromosome) {
    const floatVector = chromosome.floatVector;
    const mutation_point = Math.floor(Math.random() * floatVector.length);
    let temp = [...floatVector];
    temp[mutation_point] =
      temp[mutation_point] -
      this.mutationRange / 2 +
      Math.random() * this.mutationRange;
    return temp;
  }
```

This works as expected, returning a new data structure that can be used to build a new chromosome (chromosomes are immutable, so it will need something else to compute the fitness before we build one).

However, chromosome might or might not be a `FloatVector`, so we would need to add some type checks.

```javascript
if (chromosome.constructor.name !== "FloatChromosome") {
      throw new Error(
        `${chromosome.constructor.name} is not a FloatChromosome`
      );
    }
```

This will throw if we try to mutate a chromosome that has not been built as a `FloatChromosome`. So this code:

```javascript
const sChrom = new StringChromosome("0001", 1);
console.log(FloatChromosome.mutate(sChrom));
```

Will throw:

```text
file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30
      throw new Error(
            ^

Error: StringChromosome is not a FloatChromosome
    at FloatChromosome.mutate (file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30:13)
    at file:///home/jmerelo/Code/js/dev.to-js-types/script/mutate.js:7:29
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
```

However, these type checks are neither complete (what if it does not have a constructor?) nor precise (what if it does actually have a `floatVector` attribute, even if it's not been built with that class?). 

```javascript
const notReallyAChrom = { floatVector: [0, 0, 0, 1] };
console.log(FloatChromosome.mutate(notReallyAChrom));
```

This will still throw, although we would have been perfectly able to work with it:

```text
file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30
      throw new Error(
            ^

Error: Object is not a FloatChromosome
    at FloatChromosome.mutate (file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30:13)
    at file:///home/jmerelo/Code/js/dev.to-js-types/script/mutate.js:7:29
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
```

## Destructuring FTW

We can easily fix this using [destructuring for the function arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). Let's define the function so that it extracts from the incoming data structure just what we need, and only what we need:
```javascript
static mutate({ floatVector }) {
    if (floatVector === undefined) {
      throw new Error("Incorrect data structure: no floatVector attribute");
    }
    const mutation_point = Math.floor(Math.random() * floatVector.length);
    let temp = [...floatVector];
    temp[mutation_point] =
      temp[mutation_point] -
      this.mutationRange / 2 +
      Math.random() * this.mutationRange;
    return temp;
  }
```
This script:

```javascript
const fChrom = new FloatChromosome([0, 0, 0, 0], 0);
console.log(FloatChromosome.mutate(fChrom));

const notReallyAChrom = { floatVector: [0, 0, 0, 1] };
console.log(FloatChromosome.mutate(notReallyAChrom));

const wrongChrom = new StringChromosome("010", 3);
console.log(FloatChromosome.mutate(wrongChrom));
```

Will work correctly until it finds the last chromosome. In that case, it will produce an error. By using destructuring we find (kind of) type safety, since we will reject all invalid data structures at the same time we accept data structures we can work with, whether they are declared as a class or not.

## Coda

If you *really* want type safety, you should go for the real type-safe JavaScript, that is, [TypeScript](https://www.typescriptlang.org/). If you need to stay with JS for some reason (and there are many good reasons to do so), you can achieve a certain kind of cleanliness using this programming pattern.

You can find the final version of the code used in this paper in [this repo](https://github.com/JJ/dev.to-js-types). Previous versions can be found in the commit history.