Typescript: useDefineForClassFields breaks augmentation/overwrite of class members with decorators

Created on 13 Nov 2019  Β·  6Comments  Β·  Source: microsoft/TypeScript

TypeScript Version: 3.8.0-dev.20191113

Search Terms:
decorators, useDefineForClassFields, useDefineForClassFields decorators bug
Code

function Decorator() {
    return function (target, propKey, descriptor) {
        const symbol = Symbol();
        target.constructor.prototype[symbol] = descriptor ? typeof descriptor.initializer !== 'undefined' ? descriptor.initializer() : undefined : undefined;
        return {
            configurable: true,
            enumerable: true,
            set(value) {
                this[symbol] = value;
            },
            get() {
                return this[symbol] + 'decorated';
            }
        }
    }
}

class TestTSDefine {
    @Decorator()
    myProp = "test";
}

console.log(new TestTSDefine().myProp);

Expected behavior:
output should be 'testdecorated'

Actual behavior:
output is 'test'

Use the same code with "useDefineForClassFields = false"
output is the expected 'testdecorated'

with babel (also [[Define]] behaviour) everything works.

Playground Link: http://www.typescriptlang.org/play/?useDefineForClassFields=true&experimentalDecorators=true&ssl=1&ssc=1&pln=23&pc=40#code/GYVwdgxgLglg9mABAEQKYTgJwIZSwCgEpEBvAKEUsU1ShEyVElgUXym0wHNaAaRAA6Y4AgNKoAnvwAmqAM4RMMAXkzFyVTYgxg5URHIkBbAEZwANogC8iAMrGz5ogG4KWyh260AdDr2YQaCxvITg8KAkBVABtQ1MLAF1rRFkFJRUsRAB+RAiouGAU+UVlVW8YMBhYbHMYAC9UTEQAQisbAHJwWWAK1Gl27KK00uCKqpga+saiRAAuRC7UHrA+uYWwbt7pV3dKGjoGUjddqh0ernpsE3NUeagA1F5jk8RUMBAjRqubu4enl6oclo+AAbjUQKh1M8AR4ABYwOSxByJZJg8wQnYwgC+-xhPCgMw0MM0+3oSCg8MRcUcSQA1Ih2rIMDgoH12piAVjoVQuZouVyyBBzNg5HJEAAVeRQcW2NDLVBHTQAATQzNwBEIzyMEgACsIBMkAESsvSG1wCvwWVDecxwLj4FYAdwlUplct6RG82r1IkIziAA

* Repository to reproduce with Babel and Typescript *: https://github.com/mzeiher/decorator-ts-define-bug (see README)

Bug JS Emit Moderate Rescheduled help wanted

Most helpful comment

Ay updates here? We're increasingly seeing users complain that their components are broken because of this issue, and the symptom is very hard to track back to this unless you already know about it.

For some reason many tools that integrate the TypeScript compiler are enabling useDefineForClassFields and some of them don't give users control of the config, so there's no good workaround (except to delete own properties which puts objects into dictionary mode).

All 6 comments

Simpler example:


const Decorator: PropertyDecorator = () => {
    return {
        get() {
            return 'decorated value';
        }
    }
};

class TestTSDefine {
    @Decorator
    myProp = 'property initializer value';
}

console.log(new TestTSDefine().myProp);

http://www.typescriptlang.org/play/?useDefineForClassFields=true&experimentalDecorators=true#code/FAYw9gdgzgLgBAEQKbgE4EMZlQLjgBVTAAclUYBPZNTbOAXjgAoBKBgPjgG9g4+5USGAFdUEbr35SA5kNYSpi-oJFi4AcgAmKbJiSa4AN3QAbYUnUBuSUoC+NuPdvXQJ9FChwAKklheAysgAZgCWEEgK-AAC1LpYqA4AthSEJAxwAETERKTkFHBhITAhpiEAXmRGpuYZ1vagkFBgJkgAdCZg0kzhAO7evjABwWFIrK3JqcQs1kA

Regarding this bug, it would be nice if the typescript team could align the implementation for decorators and define semantics for fields to the current babel implementation (legacy mode) this would make a lot of things much easier especially for framework developers who ship decorator libraries (currently I'm detecting and testing TS legacy, Babel Legacy and the Babel Stage 2 implementation) a 4th decorator flavor makes it just harder to maintain (yes I know decorators are highly experimental ;))

one thing regarding inheritance, currently the useDefineForClassFields just overrides the property descriptor of super (defines a new one on the instance), it would be nicer if something like the babel helper could be used: this will just update the value but leave a possibly augmented (by a decorator or manual) intact (just the initalizer value is overridden)

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

In relation to replacing a class field/property/method - following code works on existing implementations of decorators with exception of TypeScript fields when using useDefineForClassFields. Unfortunately this is breaking without workaround. If fully aligning implementation with either babels "modern" or "legacy" decorators is too much work, perhaps consider implementing #29299 Option to disable decorator transform instead and let people further transform them themselves.

| | Fields | Properties | Methods |
|------------------------------------------------|--------|------------|---------|
| Babel "modern" | βœ… | βœ… | βœ… |
| Babel "legacy" | βœ… | βœ… | βœ… |
| TypeScript (without "useDefineForClassFields") | βœ… | βœ… | βœ… |
| TypeScript (with "useDefineForClassFields") | ⛔️ | βœ… | βœ… |

Code here:

Gist

TypeScript playground

Babel repl

Ay updates here? We're increasingly seeing users complain that their components are broken because of this issue, and the symptom is very hard to track back to this unless you already know about it.

For some reason many tools that integrate the TypeScript compiler are enabling useDefineForClassFields and some of them don't give users control of the config, so there's no good workaround (except to delete own properties which puts objects into dictionary mode).

We're still seeing this in TypeScript 4.1.0-dev.20201020. It breaks the Aurelia framework, which is blocking us from using a library that requires useDefineForClassFields.

I just ran into this because this seems to mean that it's not possible to use MobX decorators and lit-element decorators together with TypeScript. MobX decorators seem to require "useDefineForClassFields": true but that breaks lit-element decorators (like @property and @query).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kyasbal-1994 picture kyasbal-1994  Β·  3Comments

bgrieder picture bgrieder  Β·  3Comments

DanielRosenwasser picture DanielRosenwasser  Β·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  Β·  3Comments

fwanicka picture fwanicka  Β·  3Comments