Closed
Description
Summary
- Implement annotations in TypeScript.
- Align with Traceur and AtScript on syntax and ES5 representation.
- Implement feature similarly to modules, with the following settings for output representation:
- -annotations=none (default) - Annotations not supported by default. Produce compiler errors when used.
- -annotations=atscript - Annotations encoded in the style of traceur/atscript (this proposal)
- (future) -annotations=ecmascript - Hypothetically, if ES.Vnext were to adopt annotations, this would represent them using the approved ES7 annotation syntax, whatever that would be, as a bridge while TypeScript aligns with the official syntax (similar to how TS modules will transition).
- In the long term, align with ECMAScript, if an annotation proposal is accepted.
Prior Art/Reference
- Traceur and by extension AtScript (this proposal)
- Ember decorators
- Uses "- decoratorName(decoratorArgs, ...) decoratorTarget"
- Decorators can mutate the target class/field/argument(?). This proposal involves only inert metadata.
- tc-39 April 10 2014 Meeting Notes
Motivation
- This proposal improves alignment between TypeScript and AtScript (and Traceur), improving interoperability between TypeScript code and code using these features of those compilers.
- Annotations are useful in certain scenarios like dependency injection, domain driven design, decorator pattern, and more.
Differences from AtScript/Traceur
- Run-time type metadata, runtime type checking, etc... is beyond the scope of this proposal.
Syntax and ES6 Representation
I am proposing the AtScript/Traceur syntax and ES6 representations, necessarily verbatim (minus the type annotations). An annotation can be applied to a function, a class, the constructor of a class, a field of a class, or a function argument:
function:
@MyAnnotation('argument')
function func() {}
// ES6 representation
function func() {}
func.annotate = [ new MyAnnotation('argument')];
A class:
- Same representation as a function, of course.
@MyAnnotation('argument')
class MyClass {}
// ES6 representation
class MyClass {}
MyClass.annotate = [ new MyAnnotation('argument') ];
A constructor of a class:
- Note: exactly the same as annotations on the class. Constructors are listed last in the list in the ES6 representation, after class annotations.
@MyClassAnnotation('argument')
class MyClass {
@MyConstructorAnnotation('argument')
constructor() {}
}
// ES6 representation
class MyClass {
constructor() {}
}
MyClass.annotate = [ new MyClassAnnotation('argument'), new MyConstructorAnnotation('argument') ];
A field of a class:
- Note: in AtScript/Traceur, the objects to the right of the field name also have an 'is' field at the same level as the 'annotate' field. I have omitted the 'is' fields below (RTTI is out-of-scope for this proposal).
class MyClass<T> {
@MyFieldAnnotation('argument')
field: T = null;
}
// ES6 representation:
class MyClass {
field = null;
}
MyClass.properties = {
'field': { annotate: [ new MyFieldAnnotation('argument') ] }
};
Arguments to a function:
- Including arguments to constructors.
- Note: In Traceur/AtScript, objects in the parameters array also have an 'is' field which is a reference to the type of the argument. That is omitted from the following examples (RTTI is out-of-scope for this proposal).
function func<T>(@MyFuncArgumentAnnotation('argument') arg: T): void {}
class MyClass<T> {
constructor(@MyConstructorArgumentAnnotation('argument') arg: T) {}
}
// ES6 representation
function func(arg) {}
func.parameters = [ { annotate: [ new MyFuncArgumentAnnotation('argument') ] }];
class MyClass {
constructor(arg) {}
}
MyClass.parameters = [ { annotate: [ new MyConstructorArgumentAnnotation('argument') ] }];
Extensions to lib.d.ts to support typing of annotations
- e.g. adding 'properties', 'annotate', and 'parameters' field typings to Function interface.
- Can be done by a third party/DT/etc... without changes to the compiler.