1 of 29

META-PROGRAMMING IN JAVASCRIPT WITH A BIT OF HELP FROM TYPESCRIPT

Orel Balilti

November 2018

Fullstack developer @Philips Healthcare

2 of 29

About Me

  • Orel Balilti
  • orel.balilti@gmail.com

  • Fullstack developer @Philips Healthcare

3 of 29

Meta-programming

  • If programming can be described as “making programs”, metaprogramming could be described as “making programs making programs”.
  • Metaprogramming is all about the underlying mechanics of the language, rather than “high level” data modeling.
  • Meta-programming can make the development process simpler by reducing code amount

4 of 29

Descriptor – what is it?

  • The definition of a single property.
  • Defines the property value and behavior

5 of 29

Descriptor object (common usage)

  • Value - The property value to be set. (Default: undefined)
  • Accessors
    • Set – The method which should be called whenever set operation occur (=, Object.assign, etc.). (Default: undefined)
    • Get - The method which should be called whenever get operation occur (access the property). (Default: undefined)
  • Accessors and value can not be set on the same property

6 of 29

Descriptor object (common usage)

7 of 29

Descriptor object (meta usage)

  • writeable – Define if the property value can be change (readonly) (Default: false)
  • enumerable - Define if the property is visible for serialization, for loops, value assigning, etc. (Default: false)
  • configurable - Define if the property descriptor can be change (Default: false)

8 of 29

Descriptor handling

  • Object.defineProperty(target, propertyKey, descriptor})
  • Object.defineProperties(target,{[propertyKey:string]:descriptor})
  • Object.getOwnPropertyDescriptor(target, propertyKey )

9 of 29

Descriptor handling

10 of 29

Examples

11 of 29

Descriptor handling (Reflect way)

  • Reflect.defineProperty(target, propertyKey, descriptor})
  • Reflect.defineProperties(target,{[propertyKey:string]:descriptor})
  • Reflect.getOwnPropertyDescriptor(target, propertyKey ) //exactly the same as Object

12 of 29

Descriptor handling (Reflect way)

13 of 29

Examples

14 of 29

Custom behavior change with Proxy

  • Proxy use an handler for intercept object calls.
  • The handler can be define to set “traps” as handlers.
  • The proxy use the Reflect API for setting those traps.

15 of 29

Overloading operators

  • apply()
  • construct()
  • defineProperty()
  • deleteProperty()
  • set()
  • get()
  • has()
  • isExtensible()
  • ownKeys()
  • preventExtensions()

16 of 29

Examples

17 of 29

Decorator – what is it?

  • A Decorator is a special kind of declaration that can be attached to:
    • Class declaration
    • Method
    • Accessor (get, set)
    • Property
    • Parameter.

18 of 29

Class decorator

  • A Class Decorator is declared just before the class declaration.
  • The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

function Decoration(targetConstructor: Function)

19 of 29

Property decorator

  • A property decorator is declared just before a property declaration.
  • The decorator acts different for static property and instance property

function PropertyDecorator(targetObject: any, property: string)

20 of 29

Method decorator

  • Method decorator is declared just before a method declaration.
  • The decorator is applied to the property descriptor for the method and can be used to observe, modify, or replace a method definition.

function MethodDecorator(targetObject: any, property: string, property: PropertyDescriptor)

21 of 29

Reflect-metadata

  • Package for handling Metadata Reflection.
  • Allows the developer to write meta-data of a class & class properties.
  • Extends the Reflect interface:
    • Reflect.defineMetadata
    • Reflect.hasMetadata
    • Reflect.getMetadata
    • Reflect.deleteMetadata

22 of 29

Examples

23 of 29

Usage example - SugoiJS

  • SugoiJS is a modular framework which contains modules for ORM, Server and Sockets.
  • SugoiJS using abstraction and decorators syntax while using OOP and SOLID principles.
  • SugoiJS designed to reduce boilerplate and make development process easier.

24 of 29

To transform this

export class IndexController{� get(req,res,next){� authorization(req,["admin","user"],["USER.CREATE"])� .then(()=>{� })� }�� set(req,res,next){� authorization(req,["admin","user"],["USER.CREATE"])� .then(()=>{� const body = req.body;� if(!validateUserObject(body))� return res.status(400).json({message:"invalid body"})� })� .catch(err=>{� return res.status(401).json({message:"unauthorized"})� })� }�� update(req,res,next){� authorization(req,["admin","user"],["USER.CREATE"])� .then(()=>{� const id = req.param.id;� const body = req.body;� if(!validateUserObject(body) || !(!!id && typeof id === "string")� return res.status(400).json({message:"invalid params"})� User.updateById(id,body)� .then(response=>res.status(200).json(response))� .catch(err=>res.status(500).json(err));� })� .catch(err=>{� return res.status(401).json({message:"unauthorized"})� })� }�� remove(req,res,next){� authorization(req,["admin","user"],["USER.CREATE"])� .then(()=>{� })� }�}

function validateUserObject(user){� if(!(!!user.name && typeof user.name === "string")){� return false;� }� if(!(user.age != null && typeof user.age === "number" && user.age>0)){� return false;� }� return true;�}��function authorization(req,roles,permissions){� return Authorization.isAuthorized(req)� .then(()=>Authorization.allowsTo(req,permissions))� .then(()=>Authorization.isInRole(req,roles)�}

app.get("/api",IndexController.get);�app.post("/api",IndexController.set);�app.put("/api/:id",IndexController.update);�app.delete("/api:id",IndexController.remove);

25 of 29

Into this

@Authorized(["admin","user"],"USER.CREATE")�@Controller("/api")�export class IndexController{� @HttpGet("")� get(){}�� @HttpPost("")� @RequestBodySchemaPolicy(UserSchema)� async set( @RequestBody() body:User){}�� @HttpPut("/:id")� @RequestSchemaPolicy({� id:ComparableSchema.ofType(SchemaTypes.STRING).setMandatory(true)� }, null,UserSchema)� async update(@RequestParam("id") id:string,

@RequestBody() body:User){� return await User.updateById(id,body);� }�� @HttpDelete("/:id")� @RequestParamsSchemaPolicy({� id:ComparableSchema.ofType(SchemaTypes.STRING).setMandatory(true)� })� async remove(@RequestParam("id") id:string){}�}

26 of 29

Examples

27 of 29

Summary

  • Use descriptors to define object properties meta.
  • Use “Proxy” to set traps for better handling the access to objects’ properties.
  • Use decorators for encapsulate complex logic and set easier implementation.
  • Check out SugoiJS framework for pre-defined decorators.

28 of 29

Questions?

29 of 29

Resources