microsoft / microsoft/TypeScript

[decorator metadata] implicit runtime reference created

Open
#42,679 4 comments 2 reactions 1 assignee View on GitHub

@rbuckton is already working on this.

Since Feb 12, 2021.

Bug Domain: Decorators Rescheduled
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

Bug Report

🔎 Search Terms

decorator metadata shadow same name generic
(also searched in the "Domain: Decorators" tag)

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about decorators
⏯ Playground Link

Playground link with relevant code

💻 Code
import { Test } from './Test';

declare function deco(..._param: any): any;

export class Clazz {
  @deco
  method<Test>(Test: Test) {

  }
}
🙁 Actual behavior

As far as TS's unused vars logic is concerned - the above code just creates a type reference on the generic Test:
image

However when you look at the generated code:

__decorate([
    deco,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_a = typeof Test !== "undefined" && Test) === "function" ? _a : Object]),
    __metadata("design:returntype", void 0)
], Clazz.prototype, "method", null);

The decorator actually creates an implicit value reference on the import Test.

Which means if you want to satisfy the noUnusedLocals error, you will (unknowingly) change the runtime behaviour.

🙂 Expected behavior

I think this is the intended behaviour? TBH I'm not entirely sure.
I haven't found any docs about how it's supposed to work.
I think it should not be generating any runtime references.

__decorate([
    deco,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], Clazz.prototype, "method", null);

For context, in @typescript-eslint we have a scope analyser which also attempts to understand the runtime value references created by emitDecoratorMetadata. This understanding means lint rules like no-unused-vars and consistent-type-imports can understand the runtime code and provide correct lints/fixes. This logic was particularly added for consistent-type-imports, which broke people's code due to it not understanding that decorators created implicit value references.

A user presented a bug due to an (incorrect reference) - https://github.com/typescript-eslint/typescript-eslint/issues/2994.
Whilst investigating the fix, I want to ensure it's fixed correctly for all cases, including the one presented above.

If it's entirely intentional that this creates a value reference - then I'll make the referencer work the same way.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.