-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Types for Decorators Design Sync, 10/28/2022 #51347
Comments
How is the desire to enable "standalone Perhaps @addFooProperty
class C {
} would need to be rewritten as @addFooProperty
class C {
declare foo: any;
} |
Thank you for posting these notes—the subtleties are not few!
One thing I've wondered about as an initial minimal pass—for ORMs, DI, etc., it’s common to have patterns that look like these (I’m using code here from Ember and Ember Data services, but the same patterns show up in other contexts): class SomeModel {
@attr('string') name;
@attr('number') age;
@service session;
} Notably, if you wrote exactly this code the produced type is just I think the "strict subtype" rule would also allow for narrowing? That one seems a bit stranger, to be honest: class Example {
@nonNull foo: string | null;
} |
What about this? Let the converted type be declared on the decorator instead of the class field. declare function Await<T extends PromiseLike>(target: undefined, context: ClassFieldDecoratorContext): (value: T) => Awaited<T>;
declare function NonNull<T>(target: undefined, context: ClassFieldDecoratorContext): (value: T | undefined | null) => T;
declare function DependencyInject<T extends Injectable>(): (target: undefined, context: ClassFieldDecoratorContext) => (value: T) => T;
class T {
@Await field
// ~~~~~ implicit any
@Await field: number
// contextual type to infer to Await<Promise<number>>?
@Await<Promise<number>> field
// ~~~~~ number
@Await<Promise<number>> field: string
// ~~~~~~~~~~~~~~~ ~~~~~~~~ incompatible declaration
@NonNull<string | null> field
// ~~~~~ string
@Service(MyClass) field
// ~~~~~ MyClass instance
} |
Yup, leaning on type arguments and inference from the annotation type as a contextual type to the return type is discussed a bit in the notes and that's the current way we're leaning - specifically:
|
Time for me to retire now and become a duck... |
This wasn't a formal design meeting, but I figured it was worth taking notes for it.
Type Mutation for Decorators
Best Current Decorators Overview I Know Of: https://2ality.com/2022/10/javascript-decorators.html
Current Decorators Issue: #48885
Ron’s Decorators PR: #50820
Original “Decorator Mutation” Issue: #4881
Want usages of a property to be a string.
isolatedDeclarations
scenario.--isolatedDeclarations
for standalone DTS emit #47947as
) for the expression type.typeof C
refer to whenC
has any number of decorations?Seems reasonable for fields; what about signatures?
Could declare overloads?
But so if
method
is invoked somewhere else...then the signature is different?So when do you need a separate method signature?
--isolateDeclarations
.--noImplicitAny
issues.Just weird that you could write
Seems like annotation should reflect the final type?
What about a future with parameter decorators?
Need to be able to specify the type for callers, not the body of the function.
number
, body needs to witness astring
.Type mutations can be observed from several places in decorator invocations (as they capture the original target types). Modeled via type parameters.
There is a reason on top of ES standardization that we've avoided just adding the type modification behavior of decorators for years - all of this is entirely subtle.
A big part of the subtlety is that the decorator affects the containing class.
Can we avoid witnessing the entire evolution of the class?
The decorator can replace the class entirely with a function.
Could pre-allocate unresolved types on every decorator invocation so that we can defer and force resolution only when necessary.
Would it help to be able to say decorators can fully change members, but the final resulting class must be a subtype?
What are the invariants in the compiler?
Single symbol for a single entity.
Reasonable way to model this, but there needs to be a new symbol "kind" that doesn't know what kind of declaration it's resolving to.
So this would look like:
There is also
addInitializer
addInitializer
ofA1
may require the type produced by@E2
.So
final SomeClass = @E2 -> @E1 -> {@A2, @B2, @C2, D2}
Aside: is
SomeClass
TDZ?Method decorators can be functions and refer to the final incomplete state.
Seems sound, but this seems to fall over in language service scenarios.
Back to earlier topic - sometimes you want the annotation to be the original type so that you can infer for the decorator input.
Feel like we're converging on ideas, but not 100% yet. Need more discussion.
The first thing we ship will likely be a version where we forbid type mutations - maybe things being a strict subtype.
The text was updated successfully, but these errors were encountered: