question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Decorators support inconsistent with Typescript

See original GitHub issue

Bug Report

I was trying to port build pipeline (webpack) from ts-loader to babel-loader. I am using decorator (for redux action types) implementation bellow.

Current Behavior My input code works just fine with typescript. However using babel, prototype in actionClass in my decorator is undefined. I also tried various modifications (omit prototype and set type directly on actionClass) but all failed. I was simply unable to set type on the base class with decorator.

// Babel console.log(actionClass);
Descriptor {kind: "class", elements: Array(0), Symbol(Symbol.toStringTag): "Descriptor"}

Input Code

abstract class Action {
    public type: string;
    constructor() {
        this.type = this.type;
    }
}

export interface ActionClass<T extends Action> {
    prototype: T;
}

export function typeName(name: string): <T extends Action>(actionClass: ActionClass<T>) => void {
    return actionClass => {
        actionClass.prototype.type = name;
    };
}

// Usage :
export interface TestActionPayload {
    sample: string;
}
@typeName('TEST_ACTION')
export class TestAction extends Action {
    constructor(public payload: TestActionPayload) {
        super(payload);
    }
}

Expected behavior/code It is partly unclear to me if this is a bug, or problem on my end. However, one way or another, I am expecting to be able to set properties on base class with decorators. Currently I can’t.

// Typescript console.log(actionClass);
ƒ TestAction(payload) {
    var _this = _super.call(this, payload) || this;
    _this.payload = payload;
    return _this;
}

Babel Configuration (.babelrc, package.json, cli command)

options: {
    cacheDirectory: true,
    babelrc: false,
    presets: [
        [
            "@babel/preset-env",
            { targets: { browsers: "last 2 versions" } } // or whatever your project requires
        ],
        "@babel/preset-typescript",
        "@babel/preset-react"
    ],
    plugins: [
        ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
        ["@babel/plugin-proposal-class-properties", { loose: true }],
        "@babel/plugin-syntax-dynamic-import",
        "react-hot-loader/babel"
    ]
}

Environment

  • Babel version(s): [7.1.2]
  • Node/npm version: [Node 9.5.0/npm 6.4.1]
  • OS: [Windows 10]
  • Monorepo [no]
  • How you are using Babel: [loader]

Possible Solution

Additional context/Screenshots Add any other context about the problem here. If applicable, add screenshots to help explain.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:5
  • Comments:12 (3 by maintainers)

github_iconTop GitHub Comments

7reactions
trusktrcommented, Sep 10, 2020

This issue is 2 years old

There are still quite a few differences, and even after 2 years they still aren’t documented well. If I find more differences, I will document them here to start with, and then maybe I’ll put them in an article later, compadre. 😉

3reactions
trusktrcommented, Sep 8, 2020

I can’t imagine why @PeterKottas had any issue with that. It works fine for me.

But in general, the legacy decorators in Babel behave different than legacy decorators in TypeScript. For example:

  • In Babel legacy decorators, they may receive an initializer property instead of a value property, but in TypeScript there is never an initializer property, only a value property.
  • In TypeScript, decorators must return a descriptor for them to be chained with other decorators on the same class element.
  • In TypeScript, the previous point (decorator returning a descriptor) results in the class field (if decorator was used on a field) having [[Set]] semantics when useDefineForClassFields is set to false (otherwise many decorators would break).
  • In Babel, if a legacy decorator returns a descriptor, this causes the class field to always use [[Define]] semantics, regardless of the value of the loose option for the plugin-proposal-class-properties option (which seems like a bug), which is opposite of TypeScript behavior.
  • In TypeScript property decorators don’t receive a descriptor, but they do in Babel.
  • etc

And there are more differences. Overall, they are two very different decorator systems, although they both supposedly are based on the legacy spec. Maybe one evolved with the legacy spec more than the other over time?

If anyone knows of more differences in detail, please do list them here. That would be helpful!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Typescript Method Decorator has Strange Behaviour
What you're missing is that decorators do not mutate the type of the things they decorate. Since the greet() method of Foo is...
Read more >
Documentation - Decorators - TypeScript
Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. Decorators are a stage 2 proposal...
Read more >
Do decorators have a future? : r/typescript - Reddit
We generally want to avoid decorators, because they were an experimental feature that have since diverged from the TC39 proposal and have known ......
Read more >
Start Implementing Your Own Typescript Class Decorators
Your class decorator is actually a simple function that is called as a function at runtime and it gets one argument and that...
Read more >
TC39 Standards Track Decorators in Babel
TypeScript released support for decorators in version 1.5 (2015) alongside with many ES6 features. Some major frameworks, like Angular and ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found