Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Decorating JavaScript - source{d} edition

Decorating JavaScript - source{d} edition

An introduction to Class and property decorators, Stage 2, proposal.

Sergio Arbeo

June 24, 2017
Tweet

More Decks by Sergio Arbeo

Other Decks in Programming

Transcript

  1. Decorating JavaScript
    Sergio Arbeo
    Serabe
    Serabe

    View full-size slide

  2. History
    • Early work from March, 2015
    • Stage 2 for 11 months
    • PR for babylon merged 2 days ago.

    View full-size slide

  3. What is a decorator?

    View full-size slide

  4. What is a decorator?
    New Syntax

    View full-size slide

  5. What is a decorator?
    import Evented from 'evented';
    import { mixins, readOnly } from 'decorators';
    @mixins(Evented)
    class AbbaSong {
    @readOnly
    artist = 'ABBA';
    }
    export default AbbaSong;

    View full-size slide

  6. What is a decorator?
    import Evented from 'evented';
    import { mixins, readOnly } from 'decorators';
    @mixins(Evented)
    class AbbaSong {
    @readOnly
    artist = 'ABBA';
    }
    export default AbbaSong;

    View full-size slide

  7. What is a decorator?
    import Evented from 'evented';
    import { mixins, readOnly } from 'decorators';
    @mixins(Evented)
    class AbbaSong {
    @readOnly
    artist = 'ABBA';
    }
    export default AbbaSong;

    View full-size slide

  8. Syntax
    Valid
    @foo
    @foo(…args)
    @foo.bar
    @foo.bar(…args)
    @foo.bar.baz
    @foo.bar.baz(…args)

    View full-size slide

  9. Syntax
    Valid
    @foo
    @foo(…args)
    @foo.bar
    @foo.bar(…args)
    @foo.bar.baz
    @foo.bar.baz(…args)
    Invalid
    @foo[bar]
    @foo[bar](…args)
    @foo(…args).bar
    @foo(…args).bar(…args)
    @foo.bar(…args).baz
    @foo.bar(…args).baz(…args)

    View full-size slide

  10. How can I implement one?

    View full-size slide

  11. How to implement one?
    ClassDecorator:
    (Constructor,
    ParentClass,
    MemberDescriptor[]) : void

    View full-size slide

  12. How to implement one?
    MemberDecorator:
    (MemberDescriptor) : MemberDescriptor

    View full-size slide

  13. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  14. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  15. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  16. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  17. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  18. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  19. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  20. Member Descriptor
    interface MemberDescriptor {
    kind: "Property”,
    key: string,
    isStatic: boolean,
    descriptor: PropertyDescriptor,
    extras?: MemberDescriptor[],
    finisher?: (klass): void;
    }

    View full-size slide

  21. Property Descriptor
    type PropertyDescriptor =
    DataDescriptor |
    AccessorDescriptor;

    View full-size slide

  22. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  23. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  24. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  25. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  26. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  27. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  28. Property Descriptor
    interface DataDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    value?: any;
    writable?: boolean;
    }

    View full-size slide

  29. Property Descriptor
    interface AccessorDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    get?(): any;
    set?(v: any): void;
    }

    View full-size slide

  30. Property Descriptor
    interface AccessorDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    get?(): any;
    set?(v: any): void;
    }

    View full-size slide

  31. Property Descriptor
    interface AccessorDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    get?(): any;
    set?(v: any): void;
    }

    View full-size slide

  32. Property Descriptor
    interface AccessorDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    get?(): any;
    set?(v: any): void;
    }

    View full-size slide

  33. Property Descriptor
    interface AccessorDescriptor {
    configurable?: boolean;
    enumerable?: boolean;
    get?(): any;
    set?(v: any): void;
    }

    View full-size slide

  34. Property Descriptor
    type PropertyDescriptor =
    DataDescriptor |
    AccessorDescriptor;

    View full-size slide

  35. How can I implement one?

    View full-size slide

  36. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  37. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  38. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  39. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  40. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  41. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  42. Simple validator
    function validator(predicate) {
    return function(memberDescriptor) {
    let { descriptor } = memberDescriptor;
    if ('writable' in descriptor) {
    decorateData(descriptor, predicate);
    } else {
    decorateAccessor(descriptor, predicate);
    }
    return memberDescriptor;
    };
    };

    View full-size slide

  43. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  44. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  45. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  46. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  47. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  48. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  49. Simple validator
    function decorateData(descriptor, predicate) {
    let { value } = descriptor;
    descriptor.get = () => value;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    value = newValue;
    }
    };
    delete descriptor.writable;
    delete descriptor.value;
    }

    View full-size slide

  50. Simple validator
    function decorateAccessor(descriptor, predicate) {
    let { set } = descriptor;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    set(newValue);
    }
    };
    }

    View full-size slide

  51. Simple validator
    function decorateAccessor(descriptor, predicate) {
    let { set } = descriptor;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    set(newValue);
    }
    };
    }

    View full-size slide

  52. Simple validator
    function decorateAccessor(descriptor, predicate) {
    let { set } = descriptor;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    set(newValue);
    }
    };
    }

    View full-size slide

  53. Simple validator
    function decorateAccessor(descriptor, predicate) {
    let { set } = descriptor;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    set(newValue);
    }
    };
    }

    View full-size slide

  54. Simple validator
    function decorateAccessor(descriptor, predicate) {
    let { set } = descriptor;
    descriptor.set = (newValue) => {
    if (predicate(newValue)) {
    set(newValue);
    }
    };
    }

    View full-size slide

  55. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }

    View full-size slide

  56. Evaluation / Execution

    View full-size slide

  57. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }

    View full-size slide

  58. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }
    Evaluation

    View full-size slide

  59. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }
    Evaluation

    View full-size slide

  60. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }
    Execution

    View full-size slide

  61. Simple validator
    const isString = validator(
    value => typeof value === 'string’
    );
    class MyClass {
    @isString
    @validator(x => x !== '1234')
    password = "";
    }
    Execution

    View full-size slide

  62. Simple validator
    isString(
    validator(el => el === '1234')(
    passwordMemberDescriptor
    )
    )

    View full-size slide

  63. setters / getters

    View full-size slide

  64. setters / getters
    class MyClass {
    @duplicate
    get hola() {
    return 'hola';
    }
    @map( x => x.toLowerCase() )
    set hola(x) {
    send(x);
    }
    }

    View full-size slide

  65. setters / getters
    class MyClass {
    @duplicateGetter
    @mapSetter( x => x.toLowerCase() )
    get hola() {
    return 'hola';
    }
    set hola(x) {
    send(x);
    }
    }

    View full-size slide

  66. setters / getters
    class MyClass {
    get hola() {
    return 'hola';
    }
    @duplicateGetter
    @mapSetter( x => x.toLowerCase() )
    set hola(x) {
    send(x);
    }
    }

    View full-size slide

  67. Cool Things that Can Be
    Done

    View full-size slide

  68. Classics
    • Read Only properties
    • Validations
    • Computed Properties
    • Memoized methods
    • Autobinding
    • Better Singleton Classes
    • Conversion setters and getters
    • Deprecate methods
    • Debounce
    • Throttle
    • Map returned value

    View full-size slide

  69. Different express API
    @express('/posts')
    class Posts {
    @get
    index(req, res) { }
    @authorized
    @post
    create(req, res) { }
    @get(':id')
    show(req, res) { }
    }

    View full-size slide

  70. @I18ned
    class Operations {
    @i18nMethod('operations.sum')
    static sum(...args) {
    return args.reduce((a, b) => a+b, 0);
    }
    }
    Operations.sum(1, 2) // 3
    Operations.suma(2, 3) // 5
    Operations.batuketa(2, 3) // 5
    I18ned API

    View full-size slide

  71. Any questions?
    Sergio Arbeo
    Serabe
    Serabe

    View full-size slide

  72. Thank you!
    Sergio Arbeo
    Serabe
    Serabe

    View full-size slide