Improve type errors when applying a decorator to a member it does not support
#54,963 建立於 2023年7月11日
倉庫指標
- Star
- (48,455 star)
- PR 合併指標
- (平均合併 6天 17小時) (30 天內合併 9 個 PR)
描述
Suggestion
🔍 Search Terms
standard decorators auto accessor member class error message
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
The typings for standard decorators allow them to indicate through their types what they may and may not be applied to. Currently TypeScript correctly errors in these cases, but the error messages are fairly abstract. This is likely to hit people migrating from the experimental decorators particularly hard, as many property decorators will become accessor decorators, requiring decorated properties to add the auto accessor annotation.
On the Lit team we've been seeing this issue as we prepare to vend standard versions of our decorators, see related PRs https://github.com/lit/lit/pull/3982, https://github.com/lit/lit/pull/4009
📃 Motivating Example
Consider code like:
declare function decorate1(target: unknown, context: ClassAccessorDecoratorContext): void;
declare function decorate2(target: ClassAccessorDecoratorTarget<unknown, unknown>, context: ClassAccessorDecoratorContext): void;
class Foo {
@decorate1 prop1 = '';
@decorate2 prop2 = '';
}
The mistake made with this code is that prop1 and prop2 are properties, but they need to be accessors, so the fix is to change Foo to read:
class Foo {
@decorate1 accessor prop1 = '';
@decorate2 accessor prop2 = '';
}
However the type errors are not particularly helpful in indicating this! decorate1 is squiggled with:
Unable to resolve signature of property decorator when called as an expression.
Argument of type 'ClassFieldDecoratorContext<Foo, string> & { name: "prop1"; private: false; static: false; }' is not assignable to parameter of type 'ClassAccessorDecoratorContext<unknown, unknown>'.
Types of property 'kind' are incompatible.
Type '"field"' is not assignable to type '"accessor"'.
and decorate2 is squiggled with:
Unable to resolve signature of property decorator when called as an expression.
Argument of type 'undefined' is not assignable to parameter of type 'ClassAccessorDecoratorTarget<unknown, unknown>'.
It would likely require some special casing, but when there is a type error when applying a decorator, I think it would be better to give an error closer to:
decorator1 prop1 = ''
~~~~~~~~~~
'accessor' decorator is not applicable to a 'field'.
In IDEs, for the specific case of applying an accessor decorator to a field, this is an opportunity to provide the fix of adding the accessor modifier to the declaration, or remove it when applying a field decorator to an accessor.
💻 Use Cases
The experimental decorators were frequently applied to fields to run logic when a field was set (e.g. change detection, reactive rendering, etc). With standard decorators, that use case can't be met by a field decorator, an accessor decorator must be used. Auto accessors are provided to make this easy and give a good developer experience, but they're likely to only be used for decorators, and developers will not be accustomed to using them.
When a developer is updating a code base to use standard decorators, or contributing to one that's already using them, they're likely to run into this issue and be quite confused until they can find a Q&A answer about this error message somewhere.
In the meantime, we'll be documenting this issue everywhere we talk about using standard decorators with Lit.