jasnell / proposal-istypes

TC-39 Proposal for additional is{Type} APIs
201 stars 7 forks source link

What about a more general name? #7

Closed rwaldron closed 7 years ago

rwaldron commented 7 years ago

After an initial read through, I'm thinking about ways to generalize some of these names, into a single-named static method. I'm not sure what it would be named, but basically I mean something like:

ArrayBuffer.foo(new ArrayBuffer())
Map.foo(new Map()) === true;
Set.foo(new Set()) === true;
Date.foo(new Date()) === true;
RegExp.foo(new RegExp()) === true;

At the moment, that falls down on AsyncFunction and GeneratorFunction (possibly others), but those could be revisited for global object inclusion.

jasnell commented 7 years ago

I had considered something generic like isType() but, as you note, that falls down in certain cases. It's also inconsistent with the checking that already exists (e.g. Array.isArray()).

pitaj commented 7 years ago

I do think something like Reflect.isType(thing, constructor) would be a good idea to implement regardless of how the other things are implemented.

I just thought of one catch: if Reflect.isType works on non-native classes, then it may be much more difficult to implement since it's probably hard to determine if two prototypes are meant to be the same if they aren't native. Also, it may still not work for generators or async functions unless it uses something besides constructors to determine type, like maybe a table of types on the Symbol object or something?

There's been talk of a well-known Symbol-based type-checking system. Could this interfere or be redundant if that becomes a thing? I feel like maybe a proposal using Symbols as type identifiers and this proposal could merge, since they accomplish the same end goal.

Haroenv commented 7 years ago

I was thinking it might make sense to have something like Type.isArray(arg), but that probably wouldn't be the fastest way to do it behind the screens.

Thought to still let it know in case

caesarsol commented 7 years ago

The Array.isArray precedent is pretty compelling, but I must admit it's pretty redundant, and on the opposite side the idea of a nice english-readable Array.isTypeOf(variable) is attracting... 😄

ljharb commented 7 years ago

Or possibly just is?

caesarsol commented 7 years ago

I didn't propose is because it's often use in other languages to denote identity, could be confusing...

ljharb commented 7 years ago

isA :-p

caesarsol commented 7 years ago

I know isTypeOf is pretty clunky 😄

In Ruby, the most english-readable language I know of, they use:

variableName.is_a?(ClassName)        # Checks for class in inheritance chain
variableName.is_instance?(ClassName) # Checks for exact class

because the method is on the root class, Object, from which even NilClass inherits.

The problem here in JS-land is the inversion of subject and class name that makes it ugly to read...

Array is a variableName // sounds wrong
Array is the type of variableName // sounds right
Array has variableName // ?
ljharb commented 7 years ago

The middle one doesn't sound right - it's supposed to be a question, not an assertion

caesarsol commented 7 years ago

I imagined it in an if predicate, would that addition sound better?

bicknellr commented 7 years ago

IMO, Array.isArray is not a good precedent to follow. I think @pitaj's idea of reducing this proposal down to a single method on Reflect, possibly with Symbol.hasInstance-like extensibility, feels like a much better option than introducing C.isC for every C in existence.

Also, stepping back to the original proposal for a minute, I don't think these can be a static method with a common name because 'constructors' are functions and functions are objects with prototype chains as well: if Function.foo is defined and your constructor C does not implement C.foo, then C.foo returns Function.foo and thus C.foo(x) returns true for any function x. (Also, I don't think I would accept "You should know if C implements C.foo." in response, if this is meant to be the go-to way of checking types across realms.) Of course, this isn't relevant if new syntax is introduced for this concept but this problem doesn't seem to need new syntax.

ljharb commented 7 years ago

Extensibility can only work if built in types can't be made to pretend to not be themselves, and non built in types can't be made to pretend to be built in types.

In other words, what we need is a generic brand checking mechanism. We already have Symbol.hasInstance otherwise.

bicknellr commented 7 years ago

Extensibility can only work if built in types can't be made to pretend to not be themselves, and non built in types can't be made to pretend to be built in types.

FWIW, Symbol.hasInstance doesn't do either of these. (Yes, the built in ones are non-configurable, but they don't exist on the built in constructors themselves, so you are able to customize x instanceof Function, for example.) I think that if someone implements Symbol.hasInstance on their own constructor and accepts instances of built in objects, to me, that feels more like author error than a failure of Symbol.hasInstance and instanceof. Implementing Symbol.hasInstance on a built in constructor seems similar: it undermines users assumptions by breaking the "don't change built in objects" best practice. I should also mention that I'm not advocating overloading Symbol.hasInstance for this purpose, just that it's a reasonable example of similar behavior.

How does the VM learn which user-created constructors correspond across realms if this isn't implemented in JavaScript?

jasnell commented 7 years ago

Fyi... I've updated the proposal significantly, taking it in a different direction that would be easily polyfillable in pure JS. Please take a look

doodadjs commented 7 years ago

How does the VM learn which user-created constructors correspond across realms if this isn't implemented in JavaScript?

I did an experimentation with UUIDs, isolated between native and custom constructors. That works great, excepted that it doesn't prevent one re-using an UUID to pretend to be another custom constructor.

Another solution I've explored is a namespace where constructors are shared between realms. That also works great. For the moment I have to share it (the namespace) manually, but if implemented, that could be done automatically per domain.

jasnell commented 7 years ago

I believe the current proposal handles this case adequately.