Magic strings are an eyesore. I especially do not like seeing them in object bracket notation for property accessors, e.g.

abstract class Main {

run() {

const myObject = { a: 2, b: 4 };

console.log(`Dot Notation (good): ${myObject.a}`);

console.log(`Bracket Notation (bad): ${myObject['a']}`);

}

} Main.run();

The only use case I can justify using the object bracket notation is for dynamic property access, but I’d hate to use a magic string like this just to access a property like this. There is no way to guarantee that your property is going to be on the source object. The above code block has 2 main issues:

Magic strings are not necessary here, as it makes the code less maintainable and is not self-documenting. There is no guarantee that the accessed property exists on the source object.

Following the “What About Bob?” approach, let’s take baby steps and begin refactoring out magic strings:

abstract class Constants {

readonly MyProperty: string = 'a';

} abstract class Main {

run() {

const myObject = { a: 2, b: 4 };

console.log(`Better: ${myObject[Constants.MyProperty]}`);

}

}

Main.run();

No longer using a magic string, the above example is a slightly better option. It’s a more maintainable solution by creating a single source of truth, and helps to get further down the path of self-documenting code. And while we addressed the eyesore of magic strings, we still have not solved our “guaranteed property” issue.

That was a little larger than a babystep, so let’s break that down:

MyClass should look familiar to you- simply a bare-bones class. The Constants portion of this is my way of better representing constants in a large web app. You can have a 3-level organization pattern by creating a namespace to encapsulate all constants, classes to organize strings, and your static + readonly strings as constants. However, notice one subtle difference between MyProperty and MyUnsafeProperty . One uses a string type, and one strong types it to be a key of MyClass using keyof .

keyof is a Typescript type query that ensures a string exists as a property on that object (note that this applies to parent-child relationships, too). If MyProperty 's value does not exist as a field on MyClass , Typescript will give you a compiler error to let you know that your value does not exist as a field on MyClass . To be extra safe, we restrict the second parameter of _safeNameOf to keys of type T . And just in case you’ve managed to suppress this Typescript compile error, we still add an extra check in _safeNameOf to make sure the key really does exist on the source.

This is a verbose way of making your bracket notation property accessors type-safe, but it’s a great way of ensuring type security on some of the older Javascript workarounds for generic objects.