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

[helpers TS conversion] reflection #16507

Merged
merged 30 commits into from
May 19, 2024

Conversation

coelhucas
Copy link
Contributor

Q                       A
Fixed Issues? Part of #16500
Patch: Bug Fix? 👍
Major: Breaking Change? No
Minor: New Feature? No
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes? No
License MIT

Migrate all Reflection helpers to TypeScript, following the specifications. This introduces changes from yarn gulp generate-runtime-helpers

@babel-bot
Copy link
Collaborator

babel-bot commented May 17, 2024

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/56984

@coelhucas coelhucas force-pushed the migrate-reflection-helpers branch 4 times, most recently from f573af9 to 3e52aa9 Compare May 17, 2024 15:29
Comment on lines 9 to 12
type Object = {
__proto__?: any;
} & { [key: string]: unknown };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I re-used this bit in some places rather than extending object since __proto__ isn't "official", should I remove the usage of __proto__ instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not how to properly type object IMHO. I'd rather stick with (as any).__proto__ than declaring a type with a name Object.

// @ts-expect-error explicitly assign to function
_getPrototypeOf = Object.setPrototypeOf
? // @ts-expect-error -- intentionally omitting the argument
Object.getPrototypeOf.bind(/* undefined */)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Stole" these bits from #16505 approach


export default function _defineProperty<T extends Object>(
obj: T,
key: PropertyKey,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just saw the comment about using string | symbol. Will fix it here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defineProperty is different since defineProperty does allow number to be a valid object key. Using string | symbol is mistyping.

for (var i = 0; i < objectSymbols.length; i++) {
var sym = objectSymbols[i];
var objectSymbols: Array<Symbol> = Object.getOwnPropertySymbols(descs);
for (const sym of objectSymbols) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The babel 7 helpers should be ES5-compliant, so we should stick to the good old for loop.

this: unknown,
...args: [target: T, property: P, receiver?: unknown]
): P extends keyof T ? T[P] : any {
let _getImpl = _get;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the original implementation where we reassign _get, and add a @ts-expect-error to it.

This is different from the original one because the feature detection would run every single time _get is called, rather than just the fist one.

Also for the other helpers.

? // @ts-expect-error -- intentionally omitting the argument
Object.getPrototypeOf.bind(/* undefined */)
: function _getPrototypeOf<T extends Object>(o: T) {
return o.__proto__ || Object.getPrototypeOf(o);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining Object with __proto__, we can use o["__proto__"] and TS should stop complaining.

Ad an alternative, we can use

declare global {
    interface Object {
        __proto__: object;
    }
}

and then TS will automatically let us use __proto__ on things of type object (with the lowercase o)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging with interface Object is bad. If we do this, TypeScript will add __proto__ to every object out there. Let's just use (as any).__proto__, it is better than __proto__ goes rouge.

property: string | symbol,
value: any,
receiver?: any,
isStrict: boolean = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isStrict: boolean = false,
isStrict?: boolean,

This file needs to be compatible with old browsers, so let's not use default params :)

__proto__?: any;
} & { [key: string]: unknown };

export default function _getPrototypeOf<T extends Object>(o: T) {
Copy link
Contributor

@SukkaW SukkaW May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need a <T> here. Object.getPrototypeOf doesn't require a generic.

__proto__?: any;
} & { [key: string]: unknown };

export default function _objectSpread<T extends Object, U extends unknown[]>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normal object just works, don't introduce Object here.

Comment on lines 16 to 29
if (desc.set) {
if (desc && desc.set) {
desc.set.call(receiver, value);
return true;
} else if (!desc.writable) {
} else if (!desc || !desc.writable) {
Copy link
Contributor

@SukkaW SukkaW May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to guard desc here. desc will always be defined, this is an implementation detail (about where and when set will ever be called), just use !.

property: string | symbol,
value: any,
receiver?: any,
isStrict: boolean = false,
Copy link
Contributor

@SukkaW SukkaW May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use default parameters. Not only does it break the compatibility, it also increases the bundle size.


export default function _superPropBase(
object: Object,
property: string | symbol,
Copy link
Contributor

@SukkaW SukkaW May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use PropertyKey here. You are typing arguments, not typing declared variables.


function set(
target: Object,
property: string | symbol,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And PropertyKey here as well.

get: helper(
"7.0.0-beta.0",
'import superPropBase from"superPropBase";export default function _get(){return _get="undefined"!=typeof Reflect&&Reflect.get?Reflect.get.bind():function(e,t,r){var p=superPropBase(e,t);if(p){var n=Object.getOwnPropertyDescriptor(p,t);return n.get?n.get.call(arguments.length<3?e:r):n.value}},_get.apply(this,arguments)}',
'import _superPropBase from"superPropBase";export default function _get(){return _get="undefined"!=typeof Reflect&&Reflect.get?Reflect.get.bind():function(e,t,r){var p=_superPropBase(e,t);if(p){var o=Object.getOwnPropertyDescriptor(p,t);return o&&o.get?o.get.call(arguments.length<3?e:r):o?o.value:void 0}},_get.apply(this,arguments)}',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here too

@coelhucas coelhucas marked this pull request as ready for review May 17, 2024 18:27
@coelhucas coelhucas changed the title Migrate reflection helpers Migrate reflection helpers to TypeScript May 17, 2024
@nicolo-ribaudo
Copy link
Member

CI failure is not related

defineEnumerableProperties: helper(
"7.0.0-beta.0",
'export default function _defineEnumerableProperties(e,r){for(var t in r){var n=r[t];n.configurable=n.enumerable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,t,n)}if(Object.getOwnPropertySymbols)for(var a=Object.getOwnPropertySymbols(r),b=0;b<a.length;b++){var i=a[b];(n=r[i]).configurable=n.enumerable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,i,n)}return e}',
'import toPropertyKey from"toPropertyKey";export default function _defineEnumerableProperties(e,r){for(var t in r){var o=r[t];o.configurable=o.enumerable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,t,o)}if(Object.getOwnPropertySymbols)for(var n=Object.getOwnPropertySymbols(r),a=0;a<n.length;a++){var i=n[a];(o=r[i]).configurable=o.enumerable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,toPropertyKey(i),o)}return e}',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toPropertyKey is unnecessary here too, similarly to objectSpread

'import superPropBase from"superPropBase";import defineProperty from"defineProperty";function set(e,r,t,o){return set="undefined"!=typeof Reflect&&Reflect.set?Reflect.set:function(e,r,t,o){var f,i=superPropBase(e,r);if(i){if((f=Object.getOwnPropertyDescriptor(i,r)).set)return f.set.call(o,t),!0;if(!f.writable)return!1}if(f=Object.getOwnPropertyDescriptor(o,r)){if(!f.writable)return!1;f.value=t,Object.defineProperty(o,r,f)}else defineProperty(o,r,t);return!0},set(e,r,t,o)}export default function _set(e,r,t,o,f){if(!set(e,r,t,o||e)&&f)throw new TypeError("failed to set property");return t}',
'import defineProperty from"defineProperty";import superPropBase from"superPropBase";function set(e,r,t,o){return set="undefined"!=typeof Reflect&&Reflect.set?Reflect.set:function(e,r,t,o){var f,i=superPropBase(e,r);if(i){if((f=Object.getOwnPropertyDescriptor(i,r)).set)return f.set.call(o,t),!0;if(!f.writable)return!1}if(f=Object.getOwnPropertyDescriptor(o,r)){if(!f.writable)return!1;f.value=t,Object.defineProperty(o,r,f)}else defineProperty(o,r,t);return!0},set(e,r,t,o)}export default function _set(e,r,t,o,f){if(!set(e,r,t,o||e)&&f)throw new TypeError("failed to set property");return t}',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should switch the order back 😂 That causes the gzip size to increase by 2 bytes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that it doesn't actually matter much, as imports are removed when the helpers are inlined :)

#16501 will stop considering those imports in the computed size

@coelhucas coelhucas requested a review from SukkaW May 17, 2024 19:59
@liuxingbaoyu
Copy link
Member

Thanks!

@nicolo-ribaudo nicolo-ribaudo changed the title Migrate reflection helpers to TypeScript [helpers TS conversion] reflection May 18, 2024
@nicolo-ribaudo
Copy link
Member

Thanks again!

@nicolo-ribaudo nicolo-ribaudo merged commit d6c2343 into babel:main May 19, 2024
51 checks passed
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

Successfully merging this pull request may close these issues.

None yet

6 participants