DEV Community

Using JavaScript Symbol.toStringTag for objects types description

Cherif Bouchelaghem on May 04, 2021

A day ago I asked my developers friends: How to know a JavaScript object literal type? Most of the answers of the answers suggested to use inst...
Collapse
 
ninofiliu profile image
Nino Filiu

Nice, I didn't know about this!

But I wouldn't trust this trick for type-safety checking

const user = {
  name: 'John',
  [Symbol.toStringTag]: 'User',
};
delete user.name;
if (user[Symbol.toStringTag] === 'User') {
  // cool, `user` is a User, it must have the `name` property!
  user.name.length; // fails
}
Enter fullscreen mode Exit fullscreen mode

whereas in TypeScript, any operation changing the shape of an object outside of its type is prevented

type User = { name: string }
const user = { name: 'john' }
delete user.name; // Error: The operand of a 'delete' operator must be optional.(TS2790)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
danetheory profile image
Branden Dane

It's important not to forget that, at the end of the day, TypeScript isn't anything more than code sugar, pre-compilation. Post-compilation (and subsequent runtime), it's all regular ole' JavaScript. Nothing more. Nothing less. Enforcing "Type" safety in any kind of "strict" manner, will always, always, always only go so far. ECMAScript Primitives are the only datums which, natively, guarantee preserving "Type"(Undefined, Null, String, Boolean, Number, and Symbol). All remaining ECMAScript types/values fall under "Object Type" classifications/sub-classes. Why is any of this important? Objects, (be it a "POJO", function, constructor, exotic, emoji, whatever), by their very nature in JavaScript land are not ever going to be "secure", ESPECIALLY in terms of structural exposition/composition (i.e. "interface" or "shape"). However, we CAN secure "Object Type" checks and guards that remain true at runtime by placing the intended "Type" enforcements around the instance itself.

Collapse
 
cherif_b profile image
Cherif Bouchelaghem

Totally agreed! Thank you!

Collapse
 
aminnairi profile image
Amin • Edited
type User = { name: string }
const user = { name: 'john' }

Object.defineProperty(user, "name", {
  value: undefined
});

alert(user.name.length); // Cannot read property 'length' of undefined (in JavaScript after transpilation)
Enter fullscreen mode Exit fullscreen mode

This compiles just fine in TypeScript and even gives an error in the playground when run.

Collapse
 
ninofiliu profile image
Nino Filiu

Sure, but come on, that's just deliberately trying to deceive TypeScript at this point ^^

Collapse
 
cherif_b profile image
Cherif Bouchelaghem

I agree, however there's other ways to prevent this like freezing or using Proxies.

Collapse
 
levideang29 profile image
LeviDeang29

Very interesting. How I wish this was a built-in feature.

Collapse
 
cherif_b profile image
Cherif Bouchelaghem

It is built-in you just need to override the Symbol.

Collapse
 
levideang29 profile image
LeviDeang29

No I mean built-in as in you don't have to code anything else, just do a user.toStringTag() and it'll return [object user].

Collapse
 
ayoubroot profile image
ayoub benayache

interesting, ready i like the way how did you explain it, thnaks

Collapse
 
khaldiamer profile image
Khaldi Ameur

Very useful. Thanks!

Collapse
 
haouihamza profile image
hamza haoui

thank you 🙏🏻