a-proper-interface

2.3.0 • Public • Published

A Proper Interface

A more proper interface system for javascript

Note: This is currently a work in progress! There may be (possibly breaking) bugs, and updates currently do not have a schedule. If you use this module and encounter a bug, please post it on the issues page. This package will be updated many times before it is mature, and updates may not be backwards compatable. Please check for updates frequently!

Installation

npm -i a-proper-interface

or

yarn add a-proper-interface

Usage

Usage was made to be simple (as can be, given the circumstances)

Somewhere near the entrance to your code, put require('a-proper-interface') and you're good to go

Note:

the implements property is a global symbol, and needs to be accessed using brackets [implements]. The [implements] property exists on all classes extended from Implementable(), and behaves differently depending on whether it was called statically or not, but always expects an interface symbol or class (or an array of such) When called statically, it is declaring that this class implements the interface(s) provided, and constructs an instance enforced to that/those interface(s), taking optionally the second argument to [implements] as the arguments to the constructor (if the second argument is an array, it is spread into the constructor). It returns the instance created, which is enforced by the interface(s) When called on an instance, it either: (a) returns the implemented interfaces if no arguments are provided, or (b) if an argument is provided, returns a boolean indicating if the instance has been enforced to the interface(s if the argument is an array)

Declaring/Using an interface

Option 1 - Symbols

Since symbols cant be extended, using the symbols generated by this system effectively eliminates any worry of the interface being extended directly (which bypasses the enforcement of the interface). This method is faster, but has a larger footprint due to the presence of a lookup table of compiled interfaces stored internally.

//someInterface.js
class SomeInterfaceClass {}
module.exports = interface(SomeInterfaceClass)

//someImplementation.js
class SomeImplementation extends Implementable() {}
SomeImplementation[implements](require('someInterface.js'))
Option 2 - No Symbols

This method exists as an opposite to the first, bypassing symbol generation and pre-compilation of the interface class. This method has a smaller footprint, but performs slower due to having to compile the interface before enforcing it.

//someInterface.js
class SomeInterfaceClass {}
module.exports = SomeInterfaceClass

//someImplementation.js
const SomeInterface = require('./someInterface.js')
class SomeImplementation extends Implementable() {}
SomeImplementation[implements](SomeInterface)

and that's it

Everything from the number of arguments expected on functions (even the constructor) and the type of the member down to whether the member is static or not is compared and enforced

Using multiple interfaces

You can also provide an array of interfaces to implement, even mixing symbols and direct classes

//someInterface1.js
class SomeInterfaceClass1 {}
module.exports = SomeInterfaceClass1

//someInterface2.js
class SomeInterfaceClass2 {}
module.exports = SomeInterfaceClass2

//someImplementation.js
const SomeInterface1 = require('./someInterface1.js')
const SomeInterface2 = require('./someInterface2.js')
class SomeImplementation extends Implementable() {}
SomeImplementation[implements]([SomeInterface1,SomeInterface2])

Retaining the ability to extend

It might not look like it since the Implementable funtion is in the way, but extending classes is still possible by passing the class into the Implementable function

//someInterface.js
class SomeInterfaceClass {}
module.exports = interface(SomeInterfaceClass)

//someBaseClass.js
class SomeBaseClass {}
module.exports = SomeBaseClass

//someImplementation.js
const SomeInterface = require('./someInterface.js')
const BaseClass = require('./someBaseClass.js')
class SomeImplementation extends Implementable(BaseClass) {} // SomeImplementation now extends BaseClass
SomeImplementation[implements](SomeInterface)

This works because, interally, the Implementable function returns an Implementable class, and if a class is passed to the Implementable function, it extends the Implementable class from it. Otherwise, the Implementable class is extended from an empty class named EmptyBaseImplementation.

Checking if an instance implements an iterface

//someInterface.js
class SomeInterfaceClass {}
module.exports = interface(SomeInterfaceClass)

//someImplementation.js
const SomeInterface = require('./someInterface.js')
class SomeImplementation extends Implementable() {} 
const inst = SomeImplementation[implements](SomeInterface,[{test:"arguments"}])
inst[implements](SomeInterface) // true, or false 'implements' was never called statically with the interface

Todo

  • [x] Add filter to selectively enforce parts of an interface
  • [x] Add basic example
  • [x] Add system operation details in README.md Added to Usage section
  • [ ] Add in-depth examples
  • [x] Add usage in README.md
  • [x] Add demonstration of multiple interfaces in README.md
  • [x] Add demonstration of checking if class implements interface
  • [x] Add demonstration of still being able to extend classes
  • [x] Add custom errors Added as file Errors. Exported object contains all error classes
  • [x] Move functionality to separate classes to declutter index file All functionality moved to files in util
  • [x] Add license License is MIT. To add: Anyone and everyone can use/modify at will. All I ask is that the core team be reminded that interfaces need to be in javascript
  • [x] Enforce instance and static members separately implemented in filter feature
  • [ ] Add sub-interfaces
  • [x] Add better detection of global object
  • [x] Add non-global version no-globals Non-global version exports all functionality instead of attaching to global
  • [ ] Bug hunting
  • [ ] Add usage example of non-global version
  • [ ] Add utilities to aid in enforcing returns from functions/constructors that expect input

Readme

Keywords

none

Package Sidebar

Install

npm i a-proper-interface

Weekly Downloads

1

Version

2.3.0

License

MIT

Unpacked Size

36.4 kB

Total Files

17

Last publish

Collaborators

  • werlious