Currently when creating custom errors it is quite difficult to insert other data, for example consider a trivial example of a simple assertion function that checks if it's value is a string:
function assertIsString(value) {
if (typeof value !== "string") {
throw new TypeError(`Value was not a string`);
}
}
assertIsString({ x: 10, y: 20 });
This is fairly weak error message though, upon seeing the message "Value was not a string" little information is provided as to what was not a string.
Now we can try to interpolate values into our error message e.g.:
throw new TypeError(`${ value } was not a string`);
But as expected, the default stringification for most objects is just [object Object]
, not particularly useful. One could use JSON.stringify
but this again only works on objects that are valid JSON and will break completely in the precense of object cycles.
Now a number of host environments already support interpolating objects into messages via console.error
.
For example in Node for the following code we get a nicely formatted string:
const o = { x: 10, y: 20 };
console.error(`%o was not a string`, o)
{ x: 10, y: 20 } was not a string
In Chrome Devtools this is even better, as not only does it pretty print the object, but it is expandable and explorable as an object:
It would be cool if error messages supported something similar, so that when an error is shown it has nicely formatted and explorable objects.
Now there's a couple ways this could be done, either we adopt the console specifcation's formatter syntax, i.e. we would do something like:
class Error {
constructor(message: string, ...data: any[]) {
const stackMessage = HostFormatMessage(message, data);
// etc
}
}
throw new Error(`Expected %o to be a string`, value);
Or perhaps a more generic mechanism for a "formatted" message:
class Message {
static create(strings: TemplateStringsArray, ...data: any[]): Message {
return new Message(parts, data);
}
#strings: TemplateStringsArray;
#data: any[];
constructor(strings: TemplateStringsArray, data: any[]) {
this.#strings = strings;
this.#data = data;
}
toString(): string {
return HostFormatMessage(this.#string, this.#data);
}
}
throw new Error(Message.create`Expected ${ o } to be a string`);
// Could even work with other APIs e.g. all the console methods, in a cross-platform way:
console.log(Message.create(`Saw value ${ o }, adding to processing queue`));