Tired of agonizing errors caused by typos? Let's fix that!
Tags: javascript, programming
Published: 2021-09-16 05:45:10

> **Please don't use this in real-world code!**

How many times has this happened?

```js
Object.kes(obj) // => ReferenceError
```

I just **hate** it when my code is perfect except for one single typo in the middle of nowhere!

Don't you just wish the code just **_guessed_** what the correct reference was and **_just worked_**?

Well, fear not! **I have made that possible, using [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)!**

And since you don't wanna know how it works, and just wanna **_make it work_**, here's the source:

```js
const levenshtein = (str1 = '', str2 = '') => {
	const track = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));

	for (let i = 0; i <= str1.length; i += 1) track[0][i] = i;
	for (let j = 0; j <= str2.length; j += 1) track[j][0] = j;

	for (let j = 1; j <= str2.length; j += 1) {
		for (let i = 1; i <= str1.length; i += 1) {
			const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
				track[j][i] = Math.min(
				track[j][i - 1] + 1, // deletion
				track[j - 1][i] + 1, // insertion
				track[j - 1][i - 1] + indicator, // substitution
			);
		}
	}
	return track[str2.length][str1.length];
};
const closestProp = (choices, name) => {
	let lowest = Infinity;
	return choices.reduce((previous, current) => {
		const distance = levenshtein(current, name);
		if (distance < lowest) {
			lowest = distance;
			return current;
		}
		return previous;
	}, '');
};
const autofix = obj => new Proxy(obj, {
	get(obj, prop) {
		if (!(prop in obj)) prop = closestProp(Object.getOwnPropertyNames(obj), prop);
		return obj[prop];
	},
});
```

That's it!

```js
Object = autofix(Object);

Object.keys({'foo': 'bar'}); // => ['foo']
Object.kys({'foo': 'bar'}); // => ['foo']
Object.key({'foo': 'bar'}); // => ['foo']
// ...

Math = autofix(Math);
Math.PI; // => 3.141592653589793
Math.PIE; // => 3.141592653589793
```

Don't believe me? Open the DevTools and try it out!

Or here's a REPL:

{% runkit %}
const levenshtein = (str1 = '', str2 = '') => {
	const track = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));

	for (let i = 0; i <= str1.length; i += 1) track[0][i] = i;
	for (let j = 0; j <= str2.length; j += 1) track[j][0] = j;

	for (let j = 1; j <= str2.length; j += 1) {
		for (let i = 1; i <= str1.length; i += 1) {
			const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
				track[j][i] = Math.min(
				track[j][i - 1] + 1, // deletion
				track[j - 1][i] + 1, // insertion
				track[j - 1][i - 1] + indicator, // substitution
			);
		}
	}
	return track[str2.length][str1.length];
};


const closestProp = (choices, name) => {
	let lowest = Infinity;

	return choices.reduce((previous, current) => {
		const distance = levenshtein(current, name);

		if (distance < lowest) {
			lowest = distance;
			return current;
		}

		return previous;
	}, '');
};

const autofix = obj => new Proxy(obj, {
	get(obj, prop) {
		if (!(prop in obj)) prop = closestProp(Object.getOwnPropertyNames(obj), prop);
		return obj[prop];
	},
});

Math = autofix(Math);
Math.PIE;


{% endrunkit %}
<figcaption>The formatting <em>may</em> look weird</figcaption>

## How it works

This auto fixer is really simple. It does two things:

1. **We listen to property references** using a `Proxy` and **send them to a function.**
2. **When we get a reference, and the property does not exist on the object,** we try to **find the closest matching property** and return that.

Step #1 is easy, we can use a `Proxy` to do so:

```js
const autofix = obj => new Proxy(obj, {
	get(obj, prop) {
		if (!(prop in obj)) prop = closestProp(Object.getOwnPropertyNames(obj), prop)
		return obj[prop];
	},
});
```

We need to define the `closestProp` function, which is also pretty simple:

```js
const closestProp = (choices, name) => {
	let lowest = Infinity;

	return choices.reduce((previous, current) => {
		const distance = // find difference between words?

		if (distance < lowest) {
			lowest = distance;
			return current;
		}

		return previous;
	}, '');
};
```

The last part of the puzzle is to find the distance between the numbers. One way of finding the difference is by finding the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance), which is the number of single-character edits we need to make to change one word to another. 

This is an implementation of the Levenshtein distance:

```js
const levenshtein = (str1 = '', str2 = '') => {
	const track = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));

	for (let i = 0; i <= str1.length; i += 1) track[0][i] = i;
	for (let j = 0; j <= str2.length; j += 1) track[j][0] = j;

	for (let j = 1; j <= str2.length; j += 1) {
		for (let i = 1; i <= str1.length; i += 1) {
			const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
				track[j][i] = Math.min(
				track[j][i - 1] + 1, // deletion
				track[j - 1][i] + 1, // insertion
				track[j - 1][i - 1] + indicator, // substitution
			);
		}
	}
	return track[str2.length][str1.length];
};
```

Now we can add this to the `closestProp` function and we're done!

## It's a library!

I polished up the script and uploaded it to The Deno registry [here](https://deno.land/x/typosquatter). It's called `typosquatter`.

Now we can do stuff like:

```js
import typosquatter from 'https://deno.land/x/typosquatter/mod.ts';

let obj = typosquatter({foo: {bar: {baz: 'lol'}}});

console.log(obj.fo.ba.bz.substrng(1)); // => 'ol'
```

It's **recursive** and also **works for primitives!!**

---

Once again, please don't use this in real life. Actually, _maybe you can_ because it's very little overhead for solving millions of problems.

Do you think you would use this IRL? If you do, leave a comment!

And if you like the post, give this a ❤️ or a 🦄, or [share this on Twitter and spread the love!](https://twitter.com/intent/tweet?url=https%3A%2F%2Fdev.to%2Fsiddharthshyniben%2Ftired-of-agonizing-errors-caused-by-typos-let-s-fix-that-2dd4&text=Are%20you%20tired%20of%20typos%20destroying%20your%20perfect%20code%3F%20Now%20you%20can%20fix%20that%2C%20with%20just%201%20function!%20Learn%20more%20in%20this%20post%20by%20%40SiddharthShyn)