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 Slide

  2. History

    View Slide

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

    View Slide

  4. What is a decorator?

    View Slide

  5. What is a decorator?
    New Syntax

    View 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 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 Slide

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

    View Slide

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

    View Slide

  10. 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 Slide

  11. How can I implement one?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. Property Descriptor
    type PropertyDescriptor =
    DataDescriptor |
    AccessorDescriptor;

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. Property Descriptor
    type PropertyDescriptor =
    DataDescriptor |
    AccessorDescriptor;

    View Slide

  36. How can I implement one?

    View 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 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 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 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 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 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 Slide

  43. 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 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 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 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 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 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 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 Slide

  50. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. Gotchas

    View Slide

  58. Evaluation / Execution

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  65. setters / getters

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. Cool Things that Can Be
    Done

    View Slide

  70. 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 Slide

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

    View Slide

  72. @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 Slide

  73. Any questions?
    Sergio Arbeo
    Serabe
    Serabe

    View Slide

  74. Thank you!
    Sergio Arbeo
    Serabe
    Serabe

    View Slide