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

I'm wanting to resurrect this thread again.

I'm seeing all of these neat proposals go in to help solve various issues, but I feel a little - sad? - that there's also this commonly used broken feature in JavaScript that can be replaced via a small-ish proposal, but it hasn't happened yet.

Anyways, let me give another stab at this.

I feel like typeof has two main problems:

  1. The fact that the type of null is object.
  2. The fact that the intuitive way of checking if something is an object, typeof value === 'object' does not work. You have to also check if it is a function, and (because of point one), check that it is not null.

So the problem statement I'm running with is "typeof is full of landmines, let's provide a replacement that doesn't have these same two issues, and that will prevent people from ever needing to use the original typeof operator".

I'd like to propose the following example solution that tries to behave like typeof, but it also satisfies those two points. I'm not going to introduce any extra features for now in order to limit the scope (perhaps as the proposal evolves, it'll need to pick up other features as part of a larger discussion though - maybe). And I'll use syntax to satisfy @ljharb's concerns, but I'd be fine with these being functions instead.

Here we go:

// typeof.primitive returns the primitive type,
// or 'object' if it is not a primitive
typeof.primitive 2 === 'number'
typeof.primitive true === 'boolean'
typeof.primitive {} === 'object'
typeof.primitive (()=>{}) === 'object'

// typeof.function returns true if the value
// is a function.
typeof.function (()=>{}) === true
typeof.function {} === false

There's many ways we could bikeshed the API, and I'm fine doing that, but it would be nice to have something to replace typeof - it's so broken, and yet it's used so often.

You didn't show it off in your examples, but given your preceding text, I presume that typeof.primitive null === 'null', yeah?

How about this, instead, to keep the result type unified:

typeof.object {} === 'object'
typeof.object (_=>_) === 'function'

Something like that would work for me as well