Consider adding an alternative to `typeof` that doesn’t include the legacy baggage of `typeof`

This alternative would return values closer to the Type abstract operation:

Type of val Result
Undefined "undefined"
Null "null"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
BigInt "bigint"
Object "object"

Additionally, this alternative would ignore the [[IsHTMLDDA]] internal slot.

1 Like

typeof allows to distinguish between functions and non-functions, and is indeed the only way to do that reliably, although the Type abstract operation does not. That means that your new operator would not make typeof obsolete.

The two object kinds that I most often want to distinguish are functions and arrays, so that I suggest to add the following two cases:

  • "function"
  • "array" (currently testable through Array.isArray())
1 Like

Disambiguation between callables and constructors will be possible with Function.isCallable(…) and Function.isConstructor(…).

Also, Arrays should not be distinguished from plain objects in typeof, that’s what Array.isArray(…) is for.

Those aren’t unbreakable syntax, however, so robust code would continue to have to use typeof.

Just found this, because I was wanting to propose a similar idea. It's a shame we have an operator like typeof that's so broken, and no hopeful replacement for it on the horizon. We can't fix old broken features, but we can at least provide good replacements for this, and configure linter rules to prevent us from using the old ones. I also don't see any particular reason to make this an operator, we should be able to simply provide a globally available function, which I'll just call getType().

I do like the table that @ExE-Boss originally proposed. I would be against dividing "object" up into both "object" and "function". Doing so would mean that getType(value) === 'object' isn't checking if the value is an object, instead, it's checking if the value is a non-function object which is not the same thing. If we want to satisfy @claudepache's desire to distinguish arrays, functions, and other "regular" objects, perhaps we could do so by having getType() itself return an object, like this:

getType(2) // { type: 'number', objectType: null }
getType(null) // { type: 'null', objectType: null }
getType({}) // { type: 'object', objectType: 'generic' /* or something? */ }
getType([]) // { type: 'object', objectType: 'array' }
getType(() => {}) // { type: 'object', objectType: 'function' }

Something like this should satisfy the use-cases of typeof, without being that difficult to use.

May be we could use meta properties on typeof

let x: string = ''
typeof.get(x) // { type: 'string', objectType: 'string' }
typeof.is(x, 'string') // true
typeof.is(x, { type: 'string', objectType: 'string' })

Something like that would work too, and it would satisfy @ljharb's concern about robust code not being able to use it.

As a minor issue: it's easier to paint a "typeof is bad" label if the replacement functionality doesn't look like a different form of the typeof operator. In practice, I don't think this would matter that much. But, based on what I've seen in the pattern matching proposal, and how "it can't look anything like switch, because we want people to stop using switch", It's possible that similar logic would need to apply here.

How about this?

  • meta = typeof.meta(o): get built-in metadata
  • meta.type: object type: undefined, null, ordinary object, array, module namespace, function, class, string, number, HTML DDA, etc.
  • meta.isWeakKey: is usable as a weak map/set key
  • meta.isObject: is object or function
  • meta.isCallable: is callable (method, ES5 function, arrow function, or callable proxy)
  • meta.isConstructable: is constructable (class, ES5 function, constructable proxy)

This could also be extended to provide other things where useful, too, in undeniable ways.

2 Likes