IgorMinar / brainstorming

Brainstorming

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

1 Motivating examples:

1.1 Conditional implementation

Conditional code generation:

class Debug {  
    @conditional("debug")  
    static assert(condition: boolean, message?: string): void;  
}  
  
Debug.assert(false); // if window.debug is not defined Debug.assert is replaced by an empty function

1.2 Observable and computed properties

Consider the Ember.js alias-like definition:

class Person {  
    constructor(public firstName: string, public lastName: string) { }  
  
    @computed('firstName', 'lastName', (f, l) => l + ', ' + f)  
    fullName: string;  
}  
  
var david = new Person('David', 'Tang');  
david.fullName; /// Tang, David

1.3 Dynamic Instantiation/Dependency Injection (composition)

Consider Angular 2.0 DI implementation example:

class Engine {  
}  
  
class Car {  
    constructor(@Inject(Engine) engine: Engine) {}  
}  
  
var inj = new Injector([Car, Engine]);  
  
// AtScript compilation step adds a property “annotations” on Car of value [ new Inject(Engine) ].  
// At runtime, a call to inj.get would cause Angular to look for annotations, and try to satisfy dependencies.  
// in this case first by creating a new instance of Engine if it does not exist, and use it as a parameter to Car’s constructor  
var car = inj.get(Car);

1.4 Attaching Meta data to functions/objects

Metadata that can be queried at runtime, for example:

class Fixture {  
    @isTestable(true)  
    getValue(a: number): string {  
        return a.toString();  
    }  
}  
  
// Desired JS  
class Fixture {  
    getValue(a) {  
        return a.toString();  
    }  
}  
Fixture.prototype.getValue.meta.isTestable = true;  
  
// later on query the meta data  
function isTestableFunction(func) {  
    return !!(func && func.meta && func.meta.isTestable);  
}

1.5 Design-time extensibility

An extensible way to declare properties on or associate special behavior to declarations; design time tools can leverage these associations to produce errors or produce documentation. For example:

Deprecated, to support warning on use of specific API’s:

interface JQuery {  
    /**  
     * A selector representing selector passed to jQuery(), if any, when creating the original set.  
     * version deprecated: 1.7, removed: 1.9  
     */  
    @deprecated("Property is only maintained to the extent needed for supporting .live() in the jQuery Migrate plugin. It may be removed without notice in a future version.", false)  
    selector: string;  
}

Suppress linter warning:

@suppressWarning("disallow-leading-underscore")   
function __init() {  
}

2 Proposal

A decorator is defined as a function accepting the decorator’s target as an argument:

// A simple decorator  
@annotation  
class MyClass { }  
  
function annotation(target) {  
   // Add a property on target  
   target.annotated = true;  
}

Alternatively a decorator factory can be specified accepting additional arguments:

@isTestable(true)  
class MyClass { }  
  
function isTestable(value) {  
   return function decorator(target) {  
      target.isTestable = value;  
   }  
}

A decorator can also alter the target:

@log
var func = function() {
    ...
};

function log(target) {
    return function() {
        target.apply(this, arguments);
        console.log("Function called!");
    };
}

A decorator decorating a class or object literal members and accessor operate on the descriptor:

class C {  
    @enumerable(false)  
    method() { }  
}  
  
function enumerable(value) {  
    return function (target, key, propertyDescriptor) {  
       propertyDescriptor.enumerable = value;  
       return propertyDescriptor;  
    }  
}

3 Transformation details:

3.1 Introduction

This section shows the desugaring/compilation of the annotation forms to their corresponding ES5 and ES6.

3.2 Class Declaration

3.2.1 Syntax

@F("color")  
@G  
class Foo {  
}

3.2.2 Desugaring (ES6)

var Foo = (function () {  
    class Foo {  
    }  
  
    Foo = F("color")(Foo = G(Foo) || Foo) || Foo;  
    return Foo;  
})();

3.2.3 Desugaring (ES5)

var Foo = (function () {  
    function Foo() {  
    }  
  
    Foo = F("color")(Foo = G(Foo) || Foo) || Foo;  
    return Foo;  
})();

3.3 Function Expression

3.3.1 Syntax

var funcExpr = @F("color") @G function() { }

3.3.2 Desugaring

var funcExpr = (function () {  
    var _t = function() {}  
    _t = F("color")(_t = G(_t) || _t) || _t;  
    return _t;  
})();

3.4 Class Parameter Declaration

3.4.1 Syntax

class Foo {  
    constructor(@F("color") @G param0) {  
    }  
}

3.4.2 Desugaring (ES6)

var Foo = (function () {  
    class Foo {  
        constructor(param0) {  
        }  
    }  
  
    F("color")((G(Foo, 0), Foo), 0);  
    return Foo;  
})();

3.4.3 Desugaring (ES5)

var Foo = (function () {  
    function Foo(param0) {  
    }  
  
    F("color")((G(Foo, 0), Foo), 0);  
    return Foo;  
})();

3.5 Class Method Declaration

3.5.1 Syntax

class Foo {  
    @F("color")  
    @G  
    bar() { }  
}

3.5.2 Desugaring (ES6)

var Foo = (function () {  
    class Foo {  
        bar() { }  
    }  
  
    var _temp;  
    _temp = F("color")(Foo.prototype, "bar",   
        _temp = G(Foo.prototype, "bar",   
            _temp = Object.getOwnPropertyDescriptor(Foo.prototype, "bar")) || _temp) || _temp;  
    if (_temp) Object.defineProperty(Foo.prototype, "bar", _temp);  
    return Foo;  
})();

3.5.3 Desugaring (ES5)

var Foo = (function () {  
    function Foo() {  
    }  
    Foo.prototype.bar = function () { }  
  
    var _temp;  
    _temp = F("color")(Foo.prototype, "bar",   
        _temp = G(Foo.prototype, "bar",   
            _temp = Object.getOwnPropertyDescriptor(Foo.prototype, "bar")) || _temp) || _temp;  
    if (_temp) Object.defineProperty(Foo.prototype, "bar", _temp);  
    return Foo;  
})();

3.5.4 By-hand for annotations not requiring descriptors (ES5)

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.bar = function () { }

    G(Foo.prototype, "bar");
    F("color")(Foo.prototype, "bar");

    return Foo;
})();

3.6 Class Accessor Declaration

3.6.1 Syntax

class Foo {  
    @F("color")  
    @G  
    get bar() { }  
    set bar(value) { }  
}

3.6.2 Desugaring (ES6)

var Foo = (function () {  
    class Foo {  
        get bar() { }  
        set bar(value) { }  
    }  
  
    var _temp;  
    _temp = F("color")(Foo.prototype, "bar",   
        _temp = G(Foo.prototype, "bar",   
            _temp = Object.getOwnPropertyDescriptor(Foo.prototype, "bar")) || _temp) || _temp;  
    if (_temp) Object.defineProperty(Foo.prototype, "bar", _temp);  
    return Foo;  
})();

3.6.3 Desugaring (ES5)

var Foo = (function () {  
    function Foo() {  
    }  
    Object.defineProperty(Foo.prototype, "bar", {  
        get: function () { },  
        set: function (value) { }  
        enumerable: true, configurable: true  
    });  
  
    var _temp;  
    _temp = F("color")(Foo.prototype, "bar",   
        _temp = G(Foo.prototype, "bar",   
            _temp = Object.getOwnPropertyDescriptor(Foo.prototype, "bar")) || _temp) || _temp;  
    if (_temp) Object.defineProperty(Foo.prototype, "bar", _temp);  
    return Foo;  
})();

3.7 Object Literal Method Declaration

3.7.1 Syntax

var o = {  
    @F("color")  
    @G  
    bar() { }  
}

3.7.2 Desugaring (ES6)

var o = (function () {  
    var _obj = {  
        bar() { }  
    }  
  
    var _temp;  
    _temp = F("color")(_obj, "bar",   
        _temp = G(_obj, "bar",   
            _temp = void 0) || _temp) || _temp;  
    if (_temp) Object.defineProperty(_obj, "bar", _temp);  
    return Foo;  
})();

3.7.3 Desugaring (ES5)

var o = (function () {  
    var _obj = {  
        bar: function () { }  
    }  
  
    var _temp;  
    _temp = F("color")(_obj, "bar",   
        _temp = G(_obj, "bar",   
            _temp = void 0) || _temp) || _temp;  
    if (_temp) Object.defineProperty(_obj, "bar", _temp);  
    return Foo;  
})();

3.8 Object Literal Accessor Declaration

3.8.1 Syntax

var o = {  
    @F("color")  
    @G  
    get bar() { }  
    set bar(value) { }  
}

3.8.2 Desugaring (ES6)

var o = (function () {  
    var _obj = {  
        get bar() { }  
        set bar(value) { }  
    }  
  
    var _temp;  
    _temp = F("color")(_obj, "bar",   
        _temp = G(_obj, "bar",   
            _temp = void 0) || _temp) || _temp;  
    if (_temp) Object.defineProperty(_obj, "bar", _temp);  
    return Foo;  
})();

3.8.3 Desugaring (ES5)

var o = (function () {  
    var _obj = {  
    }  
    Object.defineProperty(_obj, "bar", {  
        get: function () { },  
        set: function (value) { }  
        enumerable: true, configurable: true  
    });  
  
    var _temp;  
    _temp = F("color")(_obj, "bar",   
        _temp = G(_obj, "bar",   
            _temp = void 0) || _temp) || _temp;  
    if (_temp) Object.defineProperty(_obj, "bar", _temp);  
    return Foo;  
})();

4 Examples

4.1 Inject

// TypeScript Souce
function Inject(@type type: Function) {
    return function (constructor) {
        constructor.annotate = constructor.annotate || [];
        constructor.annotate.push(new inject(type));
    }
}

class Engine {
}

class Car {
    constructor(@Inject() engine: Engine) {
    }
}


// ES3/ES5 
var Car = (function () {
    function Car(engine) {
    }

    Inject(/* @type */ Engine)(Car, /* paramterIndex */ 0);

    return Car;
})();

// ES7 decorator proposal
class Car {
    constructor(@Inject(Engine) engine) {
    }
}

4.2 Component

// TypeScript Souce
function Component(options, @parameterTypes types?: Function[]) {
    return function (constructor) {
        constructor.annotate = constructor.annotate || [];
        constructor.annotate.push(new component(options));
        constructor.parameters = types;
    }
}

@Component({
    selector: 'b'
})
class MyComponent {
    constructor(server: Server, service: Service) { }
}


// ES3/ES5 
var MyComponent = (function () {
    function MyComponent(server, service) {
    }

    MyComponent = Component({ selector: 'b' }, /* @parameterTypes */[Server, Service])(MyComponent) || MyComponent;

    return MyComponent;
})();

// ES7 decorator proposal
@Component({
    selector: 'b'
}, [Server, Service])
class MyComponent {
    constructor(server, service) { }
}

A Grammar

A.1 Expressions

  DecoratorList [Yield] :
   DecoratorList [?Yield]optDecorator [?Yield]

  Decorator [Yield] :
   @AssignmentExpression [?Yield]

  PropertyDefinition [Yield] :
   IdentifierReference [?Yield]
   CoverInitializedName [?Yield]
   PropertyName [?Yield]:AssignmentExpression [In, ?Yield]
   DecoratorList [?Yield]optMethodDefinition [?Yield]

  CoverMemberExpressionSquareBracketsAndComputedPropertyName [Yield] :
   [Expression [In, ?Yield]]

NOTE The production CoverMemberExpressionSquareBracketsAndComputedPropertyName is used to cover parsing a MemberExpression that is part of a Decorator inside of an ObjectLiteral or ClassBody, to avoid lookahead when parsing a decorator against a ComputedPropertyName.

  PropertyName [Yield, GeneratorParameter] :
   LiteralPropertyName
   [+GeneratorParameter] CoverMemberExpressionSquareBracketsAndComputedPropertyName
   [~GeneratorParameter] CoverMemberExpressionSquareBracketsAndComputedPropertyName [?Yield]

  MemberExpression [Yield]  :
   [Lexical goal InputElementRegExp] PrimaryExpression [?Yield]
   MemberExpression [?Yield]CoverMemberExpressionSquareBracketsAndComputedPropertyName [?Yield]
   MemberExpression [?Yield].IdentifierName
   MemberExpression [?Yield]TemplateLiteral [?Yield]
   SuperProperty [?Yield]
   NewSuperArguments [?Yield]
   newMemberExpression [?Yield]Arguments [?Yield]

  SuperProperty [Yield] :
   superCoverMemberExpressionSquareBracketsAndComputedPropertyName [?Yield]
   super.IdentifierName

  CallExpression [Yield] :
   MemberExpression [?Yield]Arguments [?Yield]
   SuperCall [?Yield]
   CallExpression [?Yield]Arguments [?Yield]
   CallExpression [?Yield]CoverMemberExpressionSquareBracketsAndComputedPropertyName [In, ?Yield]
   CallExpression [?Yield].IdentifierName
   CallExpression [?Yield]TemplateLiteral [?Yield]

A.4 Functions and Classes

  FormalRestParameter [Yield] :
   DecoratorList [?Yield]optBindingRestElement [?Yield]

  FormalParameter [Yield, GeneratorParameter] :
   DecoratorList [?Yield]optBindingElement [?Yield, ?GeneratorParameter]

  FunctionExpression :
   DecoratorList [?Yield]optfunctionBindingIdentifier opt(FormalParameters){FunctionBody}

  GeneratorExpression :
   DecoratorList [?Yield]optfunctionBindingIdentifier [Yield]opt(FormalParameters [Yield, GeneratorParameter]){GeneratorBody [Yield]}

  ClassDeclaration [Yield, Default] :
   DecoratorList [?Yield]optclassBindingIdentifier [?Yield]ClassTail [?Yield]
   [+Default] DecoratorList [?Yield]optclassClassTail [?Yield]

  ClassExpression [Yield, GeneratorParameter] :
   DecoratorList [?Yield]optclassBindingIdentifier [?Yield]optClassTail [?Yield, ?GeneratorParameter]

  ClassElement [Yield] :
   DecoratorList [?Yield]optMethodDefinition [?Yield]
   DecoratorList [?Yield]optstaticMethodDefinition [?Yield]

A.5 Scripts and Modules

  ExportDeclaration :
   export*FromClause;
   exportExportClauseFromClause;
   exportExportClause;
   exportVariableStatement
   exportLexicalDeclaration
   DecoratorList optexport [lookahead ≠ @] HoistableDeclaration
   DecoratorList optexport [lookahead ≠ @] ClassDeclaration
   DecoratorList optexportdefault [lookahead ≠ @] HoistableDeclaration [Default]
   DecoratorList optexportdefault [lookahead ≠ @] ClassDeclaration [Default]
   exportdefault [lookahead  { function, class, @ }] AssignmentExpression [In];

B Decorator definitions

interface TypedPropertyDescriptor<T> {  
    enumerable?: boolean;  
    configurable?: boolean;  
    writable?: boolean;  
    value?: T;  
    get?: () => T;  
    set?: (value: T) => void;  
}  
  
interface DecoratorFunction<TFunction extends Function> {  
    (target: TFunction): TFunction | void;  
}  
  
interface ArgumentDecoratorFunction {  
    (target: Function, parameterIndex: number): void;  
}  
  
interface MemberDecoratorFunction<T> {  
    (target: Function | Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;  
}  
  
interface PropertyDecoratorFunction {  
    (target: Function | Object, propertyKey: PropertyKey): void;  
}  
  
interface DecoratorFactory<TFunction extends Function> {  
    (...args: any[]): DecoratorFunction<TFunction>;  
}  
  
interface ArgumentDecoratorFactory {  
    (...args: any[]): ArgumentDecoratorFunction;  
}  
  
interface MemberDecoratorFactory<T> {  
    (...args: any[]): MemberDecoratorFunction<T>;  
}  
  
interface PropertyDecoratorFactory {  
    (...args: any[]): PropertyDecoratorFunction;  
}

C TypeScript decorators

C.1 Exposing types

TypeScript compiler will honor special decorator names and will flow additional information into the decorator factory parameters annotated by these decorators. The types provided are in a serialized form. Serialization logic is descriped in C.2

@F("color")  
class Foo {  
    constructor(a: Object, b: number, c: { a: number }, d: C2) {  
    }  
}  
  
function F(tag: string, @parameterTypes types?: Function[]) {  
    return function (target) {  
        target.parameterTypes = types; // [Object, Number, Object, C2]  
    }  
}

C.2 List of supported decorators

@type – The serialized form of the type of the decorator target

@returnType – The serialized form of the return type of the decorator target if it is a function type, undefined otherwise

@parameterTypes – A list of serialized types of the decorator target’s arguments if it is a function type, undefined otherwise

@name – The name of the decorator target

C.3 Type Serialization:

C.3.1 Example

class C { }  
interface I { }  
enum E { }  
module M { }

Formal parameter list in a call signature like so:

(a: number, b: boolean, c: C, i: I, e: E, m: typeof M, f: () => void, o: { a: number; b: string; })

Serializes as:

[Number, Boolean, C, Object, Number, Object, Function, Object]

C.3.2 Details

  • number serialized as Number
  • string serialized as String
  • boolean serialized as Boolean
  • any serialized as Object
  • void serializes as undefined
  • Array serialized as Array
  • If a Tuple, serialize as Array
  • If a class serialize it as the class constructor
  • If an Enum serialize it as Number
  • If has at least one call signature, serialize as Function
  • Otherwise serialize as Object

C.3.3 Open issues

  • Do we want to enable more elaborate serialization? E.g. type O = { a: string; b: number } as { a: String, b: Number } instead of just Object.

C.4 Defining a decorator

  • Valid decorators must be annotated by the @decorator -- details below
  • A decorator function must return a value that is assignable to what it is decorating to be considered valid.
  • @decorator parameters define the usage of the decorator and are enforced by the compiler
  • @decorator is defined in lib.d.ts and loaded by the checker when it is initialized, some constraints will be checked like that it is defined as a class with one constructor signature and one argument
  • Decorators are only allowed on functions, parameters, or members. Thus, modules, enums, and variable declarations are not valid decorator targets.
  • Constructors are not valid decorator targets. Containing classes should be used instead.
  • Decorators are emitted in declaration files attached to their declarations, normal visibility rules applies to decorators and their arguments.
/**  
  * Built-in decorator annotation. Used to define a decorator and its properties.  
  */  
declare function decorator(options?: {  
    /**  
      * Valid targets for this decorator.  
      *  
      * default: DecoratorTargets.All  
      */  
    allowOn?: DecoratorTargets;  
      
    /**  
      * Indicates whether multiple applications are allowed on the same declaration or not.  
      *  
      * default: true  
      */  
    allowMultiple?: boolean;  
  
    /**  
      * True indicates that the decorator is for design-time only, and should not be  
      * added to the emitted code.   
      *  
      * default: false  
    */  
    ambient?: boolean;  
});  
  
/**  
  * Valid decorator targets  
  */  
declare const enum DecoratorTargets {  
    Class,  
    Interface,  
    Function,  
    Method,  
    Property,  
    Accessor,  
    Parameter,  
    All = Class | Interface | Function | Method | Property | Accessor | Parameter  
}

C.5 Run-time Decorators

  • Runtime decorators are designated by setting { ambient: false } in the options of the @decorator decorator
  • Runtime decorators follow their target, decorators targeting ambient declarations are not emitted
  • Runtime decorators are not allowed on interfaces

C.6 Ambient decorators

  • Design-time (Ambient) decorators are designated by setting { ambient: true } in the options of the @decorator decorator
  • These are decorators that are not emitted. This category include built-in decorator (decorator, conditional, deprecated, etc..)
  • For design-time decorator, arguments are restricted to constant values. Variables would not be observable by the compiler at compile time. Here is the set of possible values:
    • string literal,
    • number literal,
    • regexp literal,
    • true keyword,
    • false keyword,
    • null keyword,
    • undefined symbol,
    • const enum members,
    • array literals of one of the previous kinds,
    • object literal with only properties with values of one of the previous kinds
  • Examples: @conditional, @deprecated, @profile

D Function declaration support

D.1 Function Declaration

Since decorator evaluation involved expressions, decorated function declarations cannot be hoisted to the containing scope. Rather they should be treated similar to ES6 classes where only the function’s symbol is hoisted to the top but remains undefined until the definition is encountered.

Note that decorators applied to a function declaration would necessitate a TDZ for the declaration. The desugared emit below illustrates this as the function declaration is evaluated inside an IIFE

D.1.1 Syntax

@F("color")  
@G  
class Func() {  
}

D.1.2 Desugaring

var Func = (function () {  
    function Func() {  
    }  
    Func = F("color")(Func = G(Func) || Func) || Func;  
    return Func;  
})();

D.2 Function Parameter Declaration

D.2.1 Syntax

function Func(@F("color") @G param0) {  
}

D.2.2 Desugaring

var Func = (function () {  
    function Func(param0) {  
    }  
  
    F("color")((G(Func, 0), Func), 0);  
    return Func;  
})();

D.3 Grammar

  FunctionDeclaration [Yield, Default] :
   DecoratorList [?Yield]optfunctionBindingIdentifier [?Yield](FormalParameters){FunctionBody}
   [+Default] DecoratorList [?Yield]optfunction(FormalParameters){FunctionBody}

  GeneratorDeclaration [Yield, Default] :
   DecoratorList [?Yield]optfunction*BindingIdentifier [?Yield](FormalParameters [Yield, GeneratorParameter]){GeneratorBody [Yield]}
   [+Default] DecoratorList [?Yield]optfunction*(FormalParameters [Yield, GeneratorParameter]){GeneratorBody [Yield]}

About

Brainstorming