You may have heard this in the past, but JavaScript is a loosely-typed language.

What does this mean? You can set things to arbitrary values, and the language will convert some things automatically, and try to infer what you mean– in an attempt at being flexible.

    var a = 1234; // Okay, a number.

    var b = "Hello" - a; // Uhh, what?

    console.log((b > a || b < a || b === a) ? "Yep." : "Nope");

    // Outputs "Nope." but won’t cause any errors, because it got converted
    // to a special type of Number called NaN (Not A Number)

Some cool features have made their way into the language over the years to offer keywords like const to give you the ability to make variable references immutable.

Note: that doesn’t quite apply to Arrays and Objects, because the initial reference to them is immutable, but the contents are mutable. Go figure, something else that’s complicated in the language!

You may have heard me talk about something called TypeScript, which is a superset of JavaScript that Microsoft created that gets compiled down to vanilla JS, and allows you to add type annotations to your code.

Type annotations look like this:

    // Define a type here..
    interface IProduct {
        id: string;
        name: string;
    }

    // Use it to guard the function here..
    function logProductField(providedProduct: IProduct, field: keyof IProduct): void {
        console.log(providedProduct[field]);
    }

    // Use it to guard properties/values here..
    const product: IProduct = { id: "abc123", name: "Example Product" };

    // Function call infers it here..
    logProductField(product, "name");

If you wanted to write defensive JavaScript, you might validate..

  • Provided product variable has a name,
  • Generally has the correct shape,
  • Maybe even provide thorough documentation for other developers to prevent them from making mistakes when using this function,

… but really, that’s all optional, because a developer could still make a typo or choose to do the wrong thing.

If however, you screw up in TypeScript, there’s a compiler that will catch it:

    logProductField(product, "banana");
    // Results in a compiler error:
    // [ts] Argument of type '"banana"' is not assignable to parameter of type '"id" | "name"'. [2345]

It’s pretty smart, and can infer a lot of things based on the types you’re moving around. You can even get a little more complicated, and make some flexible things called generics which can be applied to all kinds of types of things.

    // Require that what’s provided is at least an object that maps from strings to strings.
    function logObjectField<T extends Record<string, string>>(
        obj: T, field: keyof T
    ): void {
        console.log(obj[field]);
    }

The let you specify a flexible type that can be used throughout the function/code/logic, and make your code usable with many types of different object types, without needing to write a bunch of different functions for each specific type… hence the name: generics.

If you’re still reading, then you probably think this is interesting enough, here are some helpful links to learn from: