Symbols in TypeScript
In the following example we have doLog, that checks if a log properties exists on the passed in object. If yes, then it assumes it is a function and executes it.
function doLog(message: string, obj: any) {
const objStr = obj.log ? obj.log(obj) : obj.toString();
console.log(`${message} ${objStr}`);
}
doLog("The first layer: ", layer);
That’s a bad, because we cannot guarantee that log is actually a logging function. The following example would break doLog:
const layer = {
src: "dark.png",
log: true // this will break doLog
};
Another reasons why this is bad: If we iterated over the properties, log would show up, which is not what we want:
for (const key in layer) {
if (layer.hasOwnProperty(key)) {
const element = (layer as any)[key];
console.log(`${key}:${element}`);
}
}
We need to replace the property name log with something unique, that no object can accidentally overwrite: A Symbol. First, we want a function that adds a logging string to any object we pass in:
// assume you are in a logging library
const logHandler = Symbol();
function addLog<T>(obj: T, func: (obj: T) => string) {
(obj as any)[logHandler] = func;
}
Now we add the logging functionality to an object:
// in the package consumer
const layer = {
src: "dark.png",
log: true
};
addLog(layer, (obj: { src: string }) => `An image layer with src: ${obj.src}`);
Finally, when we run doLog we can be sure that log is a unique value, because we are using a Symbol.
function doLog(message: string, obj: any) {
const objStr = obj[logHandler] ? obj[logHandler](obj) : obj.toString();
console.log(`${message} ${objStr}`);
}
doLog("The first layer: ", layer);