Tired of agonizing errors caused by typos? Let's fix that!

Siddharth - Sep 16 '21 - - Dev Community

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

How many times has this happened?

Object.kes(obj) // => ReferenceError
Enter fullscreen mode Exit fullscreen mode

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!

And since you don't wanna know how it works, and just wanna make it work, here's the source:

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];
    },
});
Enter fullscreen mode Exit fullscreen mode

That's it!

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
Enter fullscreen mode Exit fullscreen mode

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

Or here's a REPL:

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;
The formatting may look weird

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:

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

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

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;
    }, '');
};
Enter fullscreen mode Exit fullscreen mode

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, 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:

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];
};
Enter fullscreen mode Exit fullscreen mode

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. It's called typosquatter.

Now we can do stuff like:

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'
Enter fullscreen mode Exit fullscreen mode

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!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .