Skip to content

Experimental (deprecated) decorators are included by default #63346

Closed as not planned

Description

@OldStarchy

⚙ Compilation target

esnext

⚙ Library

ESNext

Missing / Incorrect Definition

The old experimental decorators have (to my knowledge) always been opt-in, which means the types for those decorators are wrong by default. Now we have new decorators which are available by default, but still the old decorator types exist.

My project is using esnext lib, but it doesn't matter which version you use because they all end up including the legacy types.

The import chain is esnext -> es2024 -> es2023 -> es2022 -> es2021 -> es2020 -> es2019 -> es2018 -> es2017 -> es2016 -> es2015 -> es6 -> es5 -> decorators.legacy

In order to make my life "easier" I have defined a handful of types describing the actual function signatures of the new decorators, but my ClassDecorator (in my own types.d.ts) and the legacy decorator types are conflicting.

(See also: #16132 (comment))

Sample Code

yarn init
yarn add typescript
yarn tsx --init
touch types.d.ts
touch main.ts

types.d.ts

type ClassDecorator<
	This,
	Class extends abstract new (...args: any[]) => This,
> = (Class: Class, context: ClassDecoratorContext<Class>) => void | Class;

main.ts

// adding this import fixes the type error, but the import line itself becomes an error
// > File '[snip]/types.d.ts' is not a module.
// import type {ClassDecorator} from './types';

const foo = Symbol('foo');
const bar = Symbol('bar');
type FoobarMixin = {
	[foo]: number;
	[bar](): string;
};

function foobarMixin<
	This extends FoobarMixin,
	Class extends abstract new (...args: any) => This = abstract new (
		...args: any
	) => This,
>(): ClassDecorator<This, Class> {
	return (target: Class, _context: ClassDecoratorContext<Class>) => {
		target.prototype[foo] = 42;
		target.prototype[bar] = function () {
			return 'hello';
		};
	};
}

interface Thing extends FoobarMixin {}
@foobarMixin()
class Thing {}

console.log(new Thing()[foo]);
console.log(new Thing()[bar]());

Documentation Link

No response

Activity

  1. RyanCavanaugh commented on Apr 6, 2026

    @RyanCavanaugh
    Member

    🤖 Thank you for your issue! I've done some analysis to help get you started. This response is automatically generated; feel free to 👍 or 👎 this comment according to its usefulness.

    Similar Issues

    Here are the most similar issues I found

    If your issue is a duplicate of one of these, feel free to close this issue. Otherwise, no action is needed.

  2. RyanCavanaugh commented on Apr 7, 2026

    @RyanCavanaugh
    Member

    The error message means what it says; you are missing the export modifier on your type alias

  3. typescript-bot commented on Apr 10, 2026

    @typescript-bot
    Contributor

    This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  4. OldStarchy commented on Apr 16, 2026

    @OldStarchy
    Author

    This is not an issue about missing an export. The *.d.ts files are included by default as globals. Correct me if I'm wrong but I shouldn't to import anything from a .d.ts.
    Adding an export does not address my claim that "legacy decorators should not be included by default" so I'm afraid you've missed the point of this ticket.

    All the other types defined in .d.ts files work fine without any imports, the issue is with this particular type name conflict.

  5. OldStarchy commented on Apr 16, 2026

    @OldStarchy
    Author

    Of the other issues you listed, #29828 is the closest match, though I'm suggesting that types need removing, rather than "fixing".

    Replacing the legacy decorator types with the new ones could just invert the problem for projects that do rely on the legacy decorator syntax.

  6. added a commit that references this issue on Apr 16, 2026
    079d941
  7. RyanCavanaugh commented on Apr 16, 2026

    @RyanCavanaugh
    Member

    What I'm saying here is that you're trying to work from an invariant (any global type name should be available for usercode use) that doesn't exist, nor could exist (you can't declare "your own" Array, for example). The recommended approach for all usercode is to have your types in modules (i.e. export them), which don't have global conflicts.

  8. OldStarchy commented on Apr 19, 2026

    @OldStarchy
    Author

    Wouldn't the "invariant that does not / could not exist" be the legacy decorator types, since they do not reflect the concrete implementation provided by the compiler?

    I agree that types should be in modules for my own code, but the behavior I'm trying to capture is related to the compiler itself, its applicability is not restricted to a subset of modules. The decorator syntax is intrinsically ambient. The "xyzDecorator" types I'm trying to define are useful type-hints for defining decorators (or rather, decorator-returning functions).

    I'm not sure how the Array example is relevant here since I'm not trying to redefine any concrete types.

    Decorators work with implementation X.
    The built-in types are defined for decorators that work with implementation Y.
    Since X !== Y, the built-in types are not useful. Not only are they not useful, they're preventing developers from defining types that correctly describe implementation X in a way that makes sense for an ambient language feature.

    The point of the lib.d.ts family of files are to describe the environment in which we're programming. The legacy types don't do that.

    I'm sorry if I'm coming off as stubborn, I really don't understand why there's a desire to keep the inaccurate type definitions. Even if we were to forget about my particular naming conflict issue (and I were to start using modular types), the existing types would still be inaccurate and misleading.

  9. locked as resolved and limited conversation to collaborators on Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions