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

SWC: this returns an empy object in class constructors when fields are not initialized #32356

Closed
erikian opened this issue Dec 10, 2021 · 6 comments
Assignees
Labels
locked SWC Related to minification/transpilation in Next.js.

Comments

@erikian
Copy link

erikian commented Dec 10, 2021

What version of Next.js are you using?

12.0.7

What version of Node.js are you using?

14.16.0

What browser are you using?

Chrome

What operating system are you using?

Windows 10

How are you deploying your application?

not relevant to this issue

Describe the Bug

When accessing this in a class constructor with uninitialized fields, I'm getting an empty object instead of an object where the fields are present but undefined. This behavior is only present when SWC is enabled (i.e. no babel.config.js). When the minimum babel.config.js file (module.exports = { presets: ['next/babel'] }) is present, it works just fine.

Expected Behavior

this should return an object with all uninitialized fields present and set to undefined.

To Reproduce

Run next dev with the following files (tsconfig.json was created automatically, so I'm omitting it):

/src/pages/index.tsx

interface IUser {
  email: string
  name: string
  id: number
}

class User implements IUser {
  email: IUser['email']
  name: IUser['name']
  id: IUser['id']

  constructor(props: IUser) {
    // SWC enabled: User {}
    // SWC disabled: User {email: undefined, name: undefined, id: undefined}
    console.log(this)

    for (const [property, value] of Object.entries(props)) {
      if (Object.prototype.hasOwnProperty.call(this, property)) {
        this[property] = value
      }
    }
  }
}

export default function Home() {
  const { name, ...otherData } = new User({
    name: 'Erik',
    email: '[email protected]',
    id: 1,
    unwantedProperty: 'bar', // -> I want to filter this out
  })

  return (
    <>
      <h1>Hello {name}, here's your data:</h1>

      <ul>
        {Object.entries(otherData).map(([property, value]) => (
          <li key={property}>
            {property}: {value}
          </li>
        ))}
      </ul>
    </>
  )
}

package.json

{
  "dependencies": {
    "next": "12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "@types/react": "^17.0.37",
    "typescript": "^4.5.3"
  }
}
@erikian erikian added the bug Issue was opened via the bug report template. label Dec 10, 2021
@erikian erikian changed the title SWC: this.hasOwnProperty does not work on class constructors when fields are not initialized SWC: this returns an empy object in class constructors when fields are not initialized Dec 10, 2021
@balazsorban44 balazsorban44 added SWC Related to minification/transpilation in Next.js. kind: bug and removed bug Issue was opened via the bug report template. labels Dec 10, 2021
@erikian
Copy link
Author

erikian commented Jan 16, 2022

Just an update: looks like SWC follows tsc behavior where uninitialized fields are removed.

While they're just following a well-established convention, it doesn't really make a lot of sense to me. Consider this minimal class in plain JS:

class User {
  name
  id
}

If you console.log(new User()) on Chrome/Firefox/Node, the output will be User { name: undefined, id: undefined }, which makes sense since we're just saying this properties are there without providing any values. So one could assume that this Typescript equivalent would behave the same...

class User {
  name: string
  id: number
}

...and it does when SWC is not used. So looks like Next.js used to handle this transformation in a certain way until v11 and now it can behave in two different ways depending on which setting is used. That can be a major obstacle to migration for codebases that rely on this behavior.

@williamtetlow
Copy link

This should be fixed in swc-project/swc#3459

@erikian
Copy link
Author

erikian commented Mar 31, 2022

Still not working as of 12.1.4

@samcx samcx removed the kind: bug label Apr 30, 2024
@kdy1 kdy1 self-assigned this May 16, 2024
@kdy1
Copy link
Member

kdy1 commented May 16, 2024

See https://swc.rs/docs/migrating-from-tsc#usedefineforclassfields
This is intentional, but I'll ask to the team if we should change the behavior

@kdy1
Copy link
Member

kdy1 commented May 16, 2024

You can set compilerOptions.useDefineForClassFields to true in tsconfig.json or jsconfig.json

@kdy1 kdy1 closed this as completed May 16, 2024
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
locked SWC Related to minification/transpilation in Next.js.
Projects
None yet
Development

No branches or pull requests

5 participants