DEV Community

Sk
Sk

Posted on • Edited on

JavaScript for advanced beginners(fake js interface with proxies)

This is a rough implementation of an interface, something we'll build on top of, maybe even create something useful for developers

Simply what are proxies?

proxies are stand-in objects, they shield the real object from the "accessor", and pose as the real thing, such that any attempt to access or manipulate the real object passes thru the proxy, if the proxy allows it, it is applied to the real object vice versa. I am not going to go deep in what proxies are, because I do go over them in the eBook under the meta-programming section, extensively with multiple examples, However I am going to explain what the interface proxy does in detail

Interface?

an interface defines a shape of a particular object, such that an object that does not fit that shape or description, is not a correct object, interfaces are very useful for data validation, avoiding datatype "collision" errors, especially with JS which assumes types silently, you could end up adding a number and string in math problem, which is very catastrophic in a "real" world application, and frankly debugging types in a weakly typed language can be a nightmare, However with interfaces you can catch such error in testing,

below is a simple object, and accompying interface, as you see the interface captures the shape of the object and type of each property.

let newObj = {
    name: "sk", 
    age: 22, 
    skills:"JS, react, vue, angular, ionic", 
    anotherObj: {}
}

let interface_ = {
    name: "string", 
    age: "number", 
    skills: "string", 
    anotherObj: "object"
}

Enter fullscreen mode Exit fullscreen mode

by shape here I mean types of properties, rather than order, we don't care about order, maybe it is something we can look at later

So what we want to do is create a function that take newObj and interface_, then returns a proxy which "listens" for property changes(set), meaning newObject.name = "new name", on set the proxy must determine whether the new value is of the type defined in the interface, if you assign a number to name, it must throw an error, because name is a string


// takes in the interface and targetObj as params 
function interface(int, targetObj){

 // first we make sure the interface is an object
 if(typeof int !== "object") throw new Error("not an object")

 // a map is an object with a nicer api and more stuff, we cover in the OOJS section

 let interface_ = new Map()

//simply here we are mapping keys which correspond to keys of the real object, to their defined types
 for(let key in int){
     if(typeof int[key] !== "string") throw new Error(`${key} type   must be string`)
      // e.g name => string
     interface_.set(key, int[key])
 }

/*then we have a handler object, think of it as the stand-in object, 
traps are defined in this object, think of traps as triggers, triggered by an operation on the object, 
for example get - which simply is property access in an object, will trigger a defined get in the handler object,*/


let handler = {
      interface_,  // the map we defined above
// the ff will trap setting values on the object
      // target - is the target object
      // key and value -  {key: value} in an object
      set(target, key, value){
          // below this is just normal JS, checking if the given value matches the type defined in the interface
          let valType = typeof value;
          let keyType = this.interface_.get(key)

// if types do not match throw an error
         if(typeof value !== this.interface_.get(key)) throw new Error(`cannot assign typeof ${valType} to typeof ${keyType}`)

      // if the types match continue to set the value in the target object
        let res = Reflect.set(target, key, value)
        if(res){
            console.log("set successful")
        }
      }

  }

// return a proxy
return new Proxy(targetObj, handler)

}


Enter fullscreen mode Exit fullscreen mode

to test:

/**
 * @type {newObj}
 */
let interedObj = interface(interface_, newObj)

interedObj.name = "ur name"
interedObj.anotherObj = {}
interedObj.age = 23;
interedObj.skills = "react"


console.log(interedObj.name)


Enter fullscreen mode Exit fullscreen mode

another test if you were to try setting name to a number, it should throw an error, meaning the interface is doing it's job

interedObj.name = 23


/*
         if(typeof value !== this.interface_.get(key)) throw new Error(`cannot assign typeof ${valType} to typeof ${keyType}`)
                                                       ^

Error: cannot assign typeof number to typeof string
    at Object.set (C:\Users\Sifundo-Mhlungu\Downloads\Video\Js\OOP\interface.js:27:62)
    at Object.<anonymous> (C:\Users\Sifundo-Mhlungu\Downloads\Video\Js\OOP\interface.js:69:17)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47
*/

Enter fullscreen mode Exit fullscreen mode

If this does not make sense, don't worry, I skipped to the cool stuff, in the eBook, I do go over meta-programing basics, and more examples

also this is far from a robust interface, for one it does not check types initially but only during set, I will cover all of that

with that said this is an excerpt from an eBook I am working on: JavaScript for advanced beginners, for people who just want a little push, a well structured push towards advanced JS, there are lot of topics covered from JS code completion, OOJS, promises, Iterators and generators, meta-programming, computational media etc

it is available as a pre-order on gumroad and should release soon

Top comments (0)