Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the ability to check if something is function or class or an arrow function #178

Closed
wentout opened this issue Sep 26, 2020 · 5 comments
Closed

Comments

@wentout
Copy link

wentout commented Sep 26, 2020

Hi, folks )

In a relation to document 0000-create-element-changes.md, seems it would be nice to have an ability to check if something is class or function or an arrow function, that quotation:

The important goal here is that if we're going to introduce different semantics between classes and function components, we need to know before calling them which semantics we're going to apply.

As far as we know there might be some sort of confusion to implement this checks. But, indeed, it is possible in runtime with no usage of .toString() serialization. The short example of what does it mean to implement isClass(fn):

const isClass = (fn) => {
	if (typeof fn !== 'function') {
		return false;
	}
	if (typeof fn.prototype !== 'object') {
		return false;
	}
	if (fn.prototype.constructor !== fn) {
		return false;
	}
	return Object.getOwnPropertyDescriptor(fn, 'prototype').writable === false;
};

And the last line for isFunction would end with true keyword. This means there is strict difference between functions and classes, which might be tracked. The gist with full sum of checks is here: gist_link.

Actualy my colleague @ZeroWheel being active React developer asked me to start this issue. And we don't know if it is useful.

The last thing I'd wish to add is about known buggy behaviour of Babel.js: there is no difference between class, function or arrow function after transpilation. So, seems it might be some sort of unpredictable behaviour. Full checks are here: babel_buggy_behaviour thanks for @denis-churbanov.

This Babel behaviour comes from ancient times, when there were no classes or arrow functions. They use corejs, and it might look not so bad, but it might be strange with configuring: I'm unable to figure out with proper config of polyfilling.

And absolutely the same is TypeScript behaviour if targeting below ES2015.

Hope this information might help somehow.

@eps1lon
Copy link
Contributor

eps1lon commented Sep 27, 2020

https://overreacted.io/how-does-react-tell-a-class-from-a-function/ allows checking this. I think this is somewhat considered a public API. I couldn't find any rationale why React users should be able to do this check. The quote is written from the perspective of React internals.

The last thing I'd wish to add is about known buggy behaviour of Babel.js: there is no difference between class, function or arrow function after transpilation.

class is just syntactic sugar. Even without transpilation there's no 100% accurate way to determine if something is a class or a function.

With regards to function vs arrow function: Why should this matter? You can't distinguish that with you babel setup because you probably transpile for a target that doesn't support arrow functions.

@wentout
Copy link
Author

wentout commented Sep 27, 2020

0.

Thank you for the answer @eps1lon!

The link to how-does-react-tell-a-class-from-a-function is wonderful!
And that article is an answer to the issue! Thank you again!

There was no arguing for or against something. We were just seeking whether this useful or not. And if it might be helpful somehow, maybe, probably, perhaps. And article clearly:

  1. points to .prototype property descriptor for class: writeable === false;
  2. refers to the known stuff about babel transpilation: they are not going to give any solution;
  3. explains why isReactComponent matters: the article itself.

In relation to the other questions mentioned :


1.

rationale why React users should be able to do this check

We were just wondering if we could share some useful information for the core. And now it seems useless as its something well-known. Just because it was written here 0000-create-element-changes#deprecate-module-pattern-components

The important goal here is that if we're going to introduce different semantics between classes and function components, we need to know before calling them which semantics we're going to apply.

, and we thought it is some sort of thing that is hard to deal with.

So, finally, there is nothing to help with, it is just the way it is )


2.

class is just syntactic sugar. Even without transpilation there's no 100% accurate way to determine if something is a class or a function.

It is true that class works like sugar. And it is a bit tricky to implement class without using class keyword, especially the extends part:

  • We might be dealing with functions as classes. We might prohibit their behaviour without new keyword, using new.target. But new.target itself is a comparatively new feature.
  • Also, the .getPrototypeOf of extended class refers to the base class. And on the other hand the .prototype property itself is not re-writeable for classes. Though again, it is a new syntax from ES2015 improvements. And it might be overcomplicated journey with Object.setPrototypeOf, Object.defineProperty and 'use strict'; even having ES2015 as a starting point.
  • And before are going to deal with the above mentions, is necessity to deal with MyClass.prototype.constructor properly. And even after that there are some misconfigurations and other details with private fields and mixins. And so far is it worth getting so deep inside?

Perhaps that is why class seems to be like syntactic sugar, as transpilation does not follow each and every part of the spec. And even if it seems they are syntactic sugar, there are other parts of class constructor for example. So class keyword is not useless. But in a relation of how infrastructure of transpilation works they are not so useful, yes.


3.

With regards to function vs arrow function: Why should this matter? You can't distinguish that with you babel setup because you probably transpile for a target that doesn't support arrow functions.

Yes, yes, the same, it is clearly explained with the information in the article provided.


It is this way because it is aways transpiled to a function )

@wentout wentout closed this as completed Sep 27, 2020
@wentout
Copy link
Author

wentout commented Sep 28, 2020

There is a way to fix this behaviour:

for Arrow Functions

const workingArrow = () => {};
// right after arrow function definition
Object.defineProperty(workingArrow, 'prototype', {
    value: undefined
});

for Classes

class WorkingClass {}
// right after class definition
Object.defineProperty(WorkingClass, 'prototype', {
    writable: false
});

Just in case it might worth opening PR to babel, what do you think?

@wentout
Copy link
Author

wentout commented Sep 28, 2020

Tried to start PR for babel babel/babel#12115

@gaearon
Copy link
Member

gaearon commented Aug 24, 2021

If you have comments for that RFC, please leave it on the RFC.

@gaearon gaearon closed this as completed Aug 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants