Closed
Description
Bug Report
When using the new ts 5 decorators, decorated classes aren't usable within the same file. Compiling with the default tsconfig.json
created by tsc --init
demonstrates the issue. In the compiled output, the decorated class is added to the exports
via an anonymous function expression, but there is no reference to it created in scope for use elsewhere in the file.
Full Compled Output
"use strict";
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.push(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.push(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Example = void 0;
function loggedMethod(target, context) {
const methodName = String(context.name);
function replacementMethod(...args) {
console.log(`LOG: Entering method '${methodName}'.`);
const result = target.call(this, ...args);
console.log(`LOG: Exiting method '${methodName}'.`);
return result;
}
return replacementMethod;
}
exports.Example = (() => {
var _a;
let _instanceExtraInitializers = [];
let _getSomething_decorators;
return _a = class Example {
getSomething() {
return Math.random();
}
constructor() {
__runInitializers(this, _instanceExtraInitializers);
}
},
(() => {
_getSomething_decorators = [loggedMethod];
__esDecorate(_a, null, _getSomething_decorators, { kind: "method", name: "getSomething", static: false, private: false, access: { has: obj => "getSomething" in obj, get: obj => obj.getSomething } }, null, _instanceExtraInitializers);
})(),
_a;
})();
const example = new Example();
console.log(example.getSomething());
console.log(example.getSomething());
console.log(example.getSomething());
exports.Example = (() => {
var _a;
let _instanceExtraInitializers = [];
let _getSomething_decorators;
return _a = class Example {
getSomething() {
return Math.random();
}
constructor() {
__runInitializers(this, _instanceExtraInitializers);
}
},
(() => {
_getSomething_decorators = [loggedMethod];
__esDecorate(_a, null, _getSomething_decorators, { kind: "method", name: "getSomething", static: false, private: false, access: { has: obj => "getSomething" in obj, get: obj => obj.getSomething } }, null, _instanceExtraInitializers);
})(),
_a;
})();
Instead, this should probably emit something like:
var Example = (() => {
// ...
})();
exports.Example = Example;
🔎 Search Terms
decorated class not defined
🕗 Version & Regression Information
typescript@5.0.2
. This is the new form of decorator introduced in TS 5.x, so not a regression per se
💻 Code
This is just using the example decorator from the release announcement. It doesn't matter if the decorator is in the same file or not.
function loggedMethod<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>,
) {
const methodName = String(context.name)
function replacementMethod(this: This, ...args: Args): Return {
console.log(`LOG: Entering method '${methodName}'.`)
const result = target.call(this, ...args)
console.log(`LOG: Exiting method '${methodName}'.`)
return result
}
return replacementMethod
}
export class Example {
@loggedMethod
getSomething() {
return Math.random()
}
}
const example = new Example()
console.log(example.getSomething())
console.log(example.getSomething())
console.log(example.getSomething())
🙁 Actual behavior
/home/ehaynes99/tmp/ts5-test/dist/index.js:66
const example = new Example();
^
ReferenceError: Example is not defined
🙂 Expected behavior
The class would be available for use within the same file.