Thanks to visit codestin.com
Credit goes to github.com

Skip to content

The Node superclass setting defaults for its subclass is error prone. #1978

@Goodwine

Description

@Goodwine

The constructor for the Node class receives a map of default values, and then assigns the values to itself the following way:

this[name] = defaults[name]

This pattern is error prone because in JS, property initialization happens after calling the super() constructor. That means that even if a value is set by calling super(defaults) from a child node, the value could then be silently reset to undefined or some other initial value if the property happens to be declared in the subclass.

Example snippet in typescript:

class MyNode extends Node {
  private _foo!: string;
  get foo() { return _foo }
  set foo(f: string) { this._foo = f };

  constructor(defaults) {
    super(defaults);
  }
}

const myNode = new MyNode({foo: 'foo'});
console.log(myNode.foo); // OUTPUT: undefined

We expected the output to be "foo", but instead it was undefined because the MyNode properties are initialized after calling super(). This is JS behavior according to spec, and it can only be seen when compiling TS using the ES2022 target (or above) and enabling the tsconfig option useDefineForClassFields. The reason TS didn't fail is because TS moved the property initialization prior to calling the constructor, but it fails now that it emits more accurate JS.

The fix for this example using the current PostCSS API in this case is to replace private _foo!: string with declare _foo: string. However, this has two issues. First, it's not ideal to use declare in this case. Second, the error is silent and is only noticeable with thorough testing, which is therefore error prone because it's somewhat easy to miss.

So, I believe the PostCSS API should do something different to keep/set the default values in a different way that doesn't invite the user to write this pattern. Maybe the node super class could just keep an internal copy of the default values inside a JSON property called _defaults, and the getters/setters would just access that?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions