Is there a way to create interfaces in ES6 / Node 4?

后端 未结 6 1633
[愿得一人]
[愿得一人] 2021-01-30 08:13

ES6 is fully available in Node 4. I was wondering whether it includes a concept of interface to define method contracts as in MyClass implements MyInterface.

相关标签:
6条回答
  • 2021-01-30 08:19

    Given that ECMA is a 'class-free' language, implementing classical composition doesn't - in my eyes - make a lot of sense. The danger is that, in so doing, you are effectively attempting to re-engineer the language (and, if one feels strongly about that, there are excellent holistic solutions such as the aforementioned TypeScript that mitigate reinventing the wheel)

    Now that isn't to say that composition is out of the question however in Plain Old JS. I researched this at length some time ago. The strongest candidate I have seen for handling composition within the object prototypal paradigm is stampit, which I now use across a wide range of projects. And, importantly, it adheres to a well articulated specification.

    more information on stamps here

    0 讨论(0)
  • 2021-01-30 08:20

    Flow allows interface specification, without having to convert your whole code base to TypeScript.

    Interfaces are a way of breaking dependencies, while stepping cautiously within existing code.

    0 讨论(0)
  • 2021-01-30 08:23

    In comments debiasej wrote the mentioned below article explains more about design patterns (based on interfaces, classes):

    http://loredanacirstea.github.io/es6-design-patterns/

    Design patterns book in javascript may also be useful for you:

    http://addyosmani.com/resources/essentialjsdesignpatterns/book/

    Design pattern = classes + interface or multiple inheritance

    An example of the factory pattern in ES6 JS (to run: node example.js):

    "use strict";
    
    // Types.js - Constructors used behind the scenes
    
    // A constructor for defining new cars
    class Car {
      constructor(options){
        console.log("Creating Car...\n");
        // some defaults
        this.doors = options.doors || 4;
        this.state = options.state || "brand new";
        this.color = options.color || "silver";
      }
    }
    
    // A constructor for defining new trucks
    class Truck {
      constructor(options){
        console.log("Creating Truck...\n");
        this.state = options.state || "used";
        this.wheelSize = options.wheelSize || "large";
        this.color = options.color || "blue";
      }
    }
    
    
    // FactoryExample.js
    
    // Define a skeleton vehicle factory
    class VehicleFactory {}
    
    // Define the prototypes and utilities for this factory
    
    // Our default vehicleClass is Car
    VehicleFactory.prototype.vehicleClass = Car;
    
    // Our Factory method for creating new Vehicle instances
    VehicleFactory.prototype.createVehicle = function ( options ) {
    
      switch(options.vehicleType){
        case "car":
          this.vehicleClass = Car;
          break;
        case "truck":
          this.vehicleClass = Truck;
          break;
        //defaults to VehicleFactory.prototype.vehicleClass (Car)
      }
    
      return new this.vehicleClass( options );
    
    };
    
    // Create an instance of our factory that makes cars
    var carFactory = new VehicleFactory();
    var car = carFactory.createVehicle( {
                vehicleType: "car",
                color: "yellow",
                doors: 6 } );
    
    // Test to confirm our car was created using the vehicleClass/prototype Car
    
    // Outputs: true
    console.log( car instanceof Car );
    
    // Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
    console.log( car );
    
    var movingTruck = carFactory.createVehicle( {
                          vehicleType: "truck",
                          state: "like new",
                          color: "red",
                          wheelSize: "small" } );
    
    // Test to confirm our truck was created with the vehicleClass/prototype Truck
    
    // Outputs: true
    console.log( movingTruck instanceof Truck );
    
    // Outputs: Truck object of color "red", a "like new" state
    // and a "small" wheelSize
    console.log( movingTruck );
    
    0 讨论(0)
  • 2021-01-30 08:28

    there are packages that can simulate interfaces .

    you can use es6-interface

    0 讨论(0)
  • 2021-01-30 08:40

    Interfaces are not part of the ES6 but classes are.

    If you really need them, you should look at TypeScript which support them.

    0 讨论(0)
  • 2021-01-30 08:42

    This is my solution for the problem. You can 'implement' multiple interfaces by overriding one Interface with another.

    class MyInterface {
        // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance
        /**
         * Gives the sum of the given Numbers
         * @param {Number} a The first Number
         * @param {Number} b The second Number
         * @return {Number} The sum of the Numbers
         */
        sum(a, b) { this._WARNING('sum(a, b)'); }
    
    
        // delcare a warning generator to notice if a method of the interface is not overridden
        // Needs the function name of the Interface method or any String that gives you a hint ;)
        _WARNING(fName='unknown method') {
            console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
        }
    }
    
    class MultipleInterfaces extends MyInterface {
        // this is used for "implement" multiple Interfaces at once
        /**
         * Gives the square of the given Number
         * @param {Number} a The Number
         * @return {Number} The square of the Numbers
         */
        square(a) { this._WARNING('square(a)'); }
    }
    
    class MyCorrectUsedClass extends MyInterface {
        // You can easy use the JS doc declared in the interface
        /** @inheritdoc */
        sum(a, b) {
            return a+b;
        }
    }
    class MyIncorrectUsedClass extends MyInterface {
        // not overriding the method sum(a, b)
    }
    
    class MyMultipleInterfacesClass extends MultipleInterfaces {
        // nothing overriden to show, that it still works
    }
    
    
    let working = new MyCorrectUsedClass();
    
    let notWorking = new MyIncorrectUsedClass();
    
    let multipleInterfacesInstance = new MyMultipleInterfacesClass();
    
    // TEST IT
    
    console.log('working.sum(1, 2) =', working.sum(1, 2));
    // output: 'working.sum(1, 2) = 3'
    
    console.log('notWorking.sum(1, 2) =', notWorking.sum(1, 2));
    // output: 'notWorking.sum(1, 2) = undefined'
    // but also sends a warn to the console with 'WARNING! Function "sum(a, b)" is not overridden in MyIncorrectUsedClass'
    
    console.log('multipleInterfacesInstance.sum(1, 2) =', multipleInterfacesInstance.sum(1, 2));
    // output: 'multipleInterfacesInstance.sum(1, 2) = undefined'
    // console warn: 'WARNING! Function "sum(a, b)" is not overridden in MyMultipleInterfacesClass'
    
    console.log('multipleInterfacesInstance.square(2) =', multipleInterfacesInstance.square(2));
    // output: 'multipleInterfacesInstance.square(2) = undefined'
    // console warn: 'WARNING! Function "square(a)" is not overridden in MyMultipleInterfacesClass'
    

    EDIT:

    I improved the code so you now can simply use implement(baseClass, interface1, interface2, ...) in the extend.

    /**
    * Implements any number of interfaces to a given class.
    * @param cls The class you want to use
    * @param interfaces Any amount of interfaces separated by comma
    * @return The class cls exteded with all methods of all implemented interfaces
    */
    function implement(cls, ...interfaces) {
        let clsPrototype = Object.getPrototypeOf(cls).prototype;
        for (let i = 0; i < interfaces.length; i++) {
            let proto = interfaces[i].prototype;
            for (let methodName of Object.getOwnPropertyNames(proto)) {
                if (methodName!== 'constructor')
                    if (typeof proto[methodName] === 'function')
                        if (!clsPrototype[methodName]) {
                            console.warn('WARNING! "'+methodName+'" of Interface "'+interfaces[i].name+'" is not declared in class "'+cls.name+'"');
                            clsPrototype[methodName] = proto[methodName];
                        }
            }
        }
        return cls;
    }
    
    // Basic Interface to warn, whenever an not overridden method is used
    class MyBaseInterface {
        // declare a warning generator to notice if a method of the interface is not overridden
        // Needs the function name of the Interface method or any String that gives you a hint ;)
        _WARNING(fName='unknown method') {
            console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
        }
    }
    
    
    // create a custom class
    /* This is the simplest example but you could also use
    *
    *   class MyCustomClass1 extends implement(MyBaseInterface) {
    *       foo() {return 66;}
    *   }
    *
    */
    class MyCustomClass1 extends MyBaseInterface {
        foo() {return 66;}
    }
    
    // create a custom interface
    class MyCustomInterface1 {
         // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance
    
        /**
         * Gives the sum of the given Numbers
         * @param {Number} a The first Number
         * @param {Number} b The second Number
         * @return {Number} The sum of the Numbers
         */
        sum(a, b) { this._WARNING('sum(a, b)'); }
    }
    
    // and another custom interface
    class MyCustomInterface2 {
        /**
         * Gives the square of the given Number
         * @param {Number} a The Number
         * @return {Number} The square of the Numbers
         */
        square(a) { this._WARNING('square(a)'); }
    }
    
    // Extend your custom class even more and implement the custom interfaces
    class AllInterfacesImplemented extends implement(MyCustomClass1, MyCustomInterface1, MyCustomInterface2) {
        /**
        * @inheritdoc
        */
        sum(a, b) { return a+b; }
    
        /**
        * Multiplies two Numbers
        * @param {Number} a The first Number
        * @param {Number} b The second Number
        * @return {Number}
        */
        multiply(a, b) {return a*b;}
    }
    
    
    // TEST IT
    
    let x = new AllInterfacesImplemented();
    
    console.log("x.foo() =", x.foo());
    //output: 'x.foo() = 66'
    
    console.log("x.square(2) =", x.square(2));
    // output: 'x.square(2) = undefined
    // console warn: 'WARNING! Function "square(a)" is not overridden in AllInterfacesImplemented'
    
    console.log("x.sum(1, 2) =", x.sum(1, 2));
    // output: 'x.sum(1, 2) = 3'
    
    console.log("x.multiply(4, 5) =", x.multiply(4, 5));
    // output: 'x.multiply(4, 5) = 20'
    
    0 讨论(0)
提交回复
热议问题