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

(fix) Class fields are set to undefined if there's no initializer #3459

Merged

Conversation

williamtetlow
Copy link
Contributor

@williamtetlow williamtetlow commented Feb 5, 2022

Description:
Fixes #2117 by returning null instead of void 0 for an initializer that is not set on property descriptor. See this comment for good description of problem

This caused tests to fail for #2127 but I have provided alternative fix through checking for declare modifier on fields that shouldn't be emitted.

TS ESNext now emits class fields with no initializer by default. See useDefineForClassFields

class AClass {
  field: String
}

// emits (pre-esnext)
class AClass {}

// emits (esnext)
class AClass {
  field
}

It supports overriding this behaviour through the declare modifier.

class AClass {
  declare field: String
}

// emits (esnext)
class AClass {}

#960 and #2127 should now be solved by using declare modifier. Swc will emit class fields by default now to match tsc behaviour.

BREAKING CHANGE:
#960
#2127

By default, class fields will be defined and set to undefined breaking the two issues above.

Fields should have declare modifier if they shouldn't be emitted.

class TestClass2 {
  @deco public declare testProperty: Date;
}

Related issue (if exists):
#2117
#2127
#960

@CLAassistant
Copy link

CLAassistant commented Feb 5, 2022

CLA assistant check
All committers have signed the CLA.

@kdy1
Copy link
Member

kdy1 commented Feb 5, 2022

If I remeber correctly, this is wrong

@williamtetlow williamtetlow marked this pull request as ready for review February 5, 2022 23:03
@williamtetlow
Copy link
Contributor Author

williamtetlow commented Feb 5, 2022

@kdy1 I had a look into this issue and how to fix it while keeping fix for #2127

See 46d4cd8

For #2127 I compiled the same code with SWC, Babel, esbuild and TSC.

SWC - ✅
Babel - ❌
TSC (esnext) - ❌
TSC (<esnext) - ✅
esbuild - ❌

Looks like only SWC and TSC (lower than esnext) supports this at the moment all the others return undefined for the property.

I read the spec to see if there was a mention of how this should be handled (e.g. no initializer always sets value to undefined) but there wasn't anything.

However, I found an alternative way to fix #2127 and moved it to _initializerDefineProperty.

If there is no initializer for the property and the property already has a value that != void 0 then it will set it to the current value. Otherwise, set it to void 0.

If object does not have own property it will check the prototype chain.

@williamtetlow williamtetlow force-pushed the class-property-decorators-missing branch from 396f753 to 9644911 Compare February 5, 2022 23:10
@williamtetlow
Copy link
Contributor Author

williamtetlow commented Feb 6, 2022

I've pushed my local test bench for this issue which shows how each tool handles both issues https://github.com/williamtetlow/2117-2127-swc-testbench

For #2127 the reason this code worked for the issue raiser on ts-jest is because ts-jest uses TSC and they were using es5 which supports #2127 but does not support #2117

Screenshot 2022-02-06 at 11 57 58

@kdy1
Copy link
Member

kdy1 commented Feb 6, 2022

Oh... Then I think we should follow esnext behavior of tsc.

@williamtetlow
Copy link
Contributor Author

williamtetlow commented Feb 6, 2022

It looks like it's related to useDefineForClassFields which is for migrating to new TC39 standard version of class fields.

This is turned on by default for esnext.

The issue in #2127 is that the field is defined on prototype.constructor but by defining the field on the class too the original is hidden.

((_class = class TestClass2 {
    constructor() {
      // defining here hides prototype.constructor.testProperty
      _initializerDefineProperty(this, "testProperty", _descriptor, this);
    }
  })

To help mitigate the second issue, you can either add an explicit initializer or add a declare modifier to indicate that a property should have no emit.

Going forward tsc will define class fields by default (like swc is doing) but you can override this and not emit by adding declare modifier to a property.

class TestClass2 {
  @deco public declare testProperty: Date;
}

I've added this logic to swc in 813e3eb#diff-8f03d343376a656cd03aec743c8cb8b60d7c1a4de29f48d940be0795c0931235R676

@williamtetlow
Copy link
Contributor Author

williamtetlow commented Feb 6, 2022

I'm not sure if there is a way to opt out of this logic for Ecmascript unless we make a config setting.

Should we add setting or are we ok with making #2127 and #960 not work anymore when compiling Ecmascript to better replicate Typescript behaviour?

We could add setting to jsc.transform and then we can allow disabling emit logic for Typescript too

@kdy1
Copy link
Member

kdy1 commented Feb 6, 2022

Should we add setting or are we ok with making #2127 and #960 not work anymore when compiling Ecmascript to better replicate Typescript behaviour?

I think it's fine, and it's the right way

@williamtetlow williamtetlow changed the title (fix) Decorators on class properties missing (fix) Class fields are set to undefined if there's no initializer Feb 6, 2022
@kdy1
Copy link
Member

kdy1 commented Feb 7, 2022

Is this PR ready for review/merge?

@williamtetlow
Copy link
Contributor Author

@kdy1 yep it's ready 😄

@kdy1 kdy1 added this to the v1.2.137 milestone Feb 7, 2022
@williamtetlow williamtetlow requested a review from kdy1 February 7, 2022 15:02
Copy link
Member

@kdy1 kdy1 left a comment

Choose a reason for hiding this comment

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

Thanks!

@kdy1 kdy1 enabled auto-merge (squash) February 7, 2022 15:15
@kdy1 kdy1 merged commit 4f5e87b into swc-project:main Feb 7, 2022
@williamtetlow williamtetlow deleted the class-property-decorators-missing branch February 7, 2022 16:12
@aboqasem
Copy link

When could this fix land in Next.js?

@kdy1
Copy link
Member

kdy1 commented Feb 23, 2022

@aboqasem I'll make a PR today.

@aboqasem
Copy link

@kdy1 Thank you for your effort!

@JCMais
Copy link

JCMais commented Apr 16, 2022

Did this land on Next.js?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Class transformer not working with swc
5 participants