interface
!travistravis-imgtravis-url !daviddavid-imgdavid-url !codacycodacy-imgcodacy-url !minifiedminified-imgunpkg-url !gzippedgzipped-imgunpkg-url !Code Stylexo-imgxo-url !NPM Versionnpm-imgnpm-urlInterfaces for JavaScript. Sort of.
Setup
$ npm install @darkobits/interface
The
Interface
class allows for the creation of contracts between an object and its consumers. Interfaces are constructed with a name and an optional list of argument types. This package also exports the Any
value which can be used to indicate a desired minimum arity of an interface's implementation without enforcing argument types.The value returned from the constructor can then be used in two ways:
- To implement the interface on classes/objects using
implementedBy
. - As a key that can be used to invoke the interface on objects that implement it.
Interface
performs a minimum arity check when implementations are registered and performs minimum arity and type-checking against invocations of implementations at runtime.Note: Implementations are bound to their host objects, so in most cases arrow functions should not be used to define them.
Using with Classes
When provided a class or constructor function,Interface
will install implementations on its prototype
. In most cases, this is desired.import Interface from '@darkobits/interface';
// Create a new interface, SetName, that should be used with one string argument.
const SetName = new Interface('SetName', [String]);
class Person {
constructor () {
this.name = '';
}
getName () {
return this.name;
}
}
// This implementation doesn't meet the minimum arity for the interface, so it will throw an error:
SetName.implementedBy(Person).as(function () {
});
// This will pass:
SetName.implementedBy(Person).as(function (str) {
this.name = str;
});
// The interface can then be used thusly:
const frodo = new Person();
frodo[SetName](); // Arity check will fail, this will throw an error.
frodo[SetName](null); // Type check will fail, this will throw an error.
frodo[SetName]('Frodo Baggins'); // This will pass.
Using with Instances
In some cases, however, the interface may need to have access to a constructor function's closure. When passed an object,Interface
will install the implementation onto the object directly.import Interface from '@darkobits/interface';
const SetName = new Interface('SetName', [String]);
function Person () {
let name = '';
this.getName = () => {
return name;
};
SetName.implementedBy(this).as(function (str) {
name = str;
});
}
const frodo = new Person();
frodo[SetName]('Frodo Baggins');
Using the Any
Placeholder
import {
Any,
Interface
} from '@darkobits/interface';
// Create an interface to set a key/value pair. Keys should be strings, but values can be anything.
const SetData = new Interface('SetData', [String, Any]);
class Model {
constructor () {
this.data = {};
}
getData (key) {
return this.data[key];
}
}
SetData.implementedBy(Model).as(function (key, value) {
this.data[key] = value;
});
const myModel = new Model();
myModel[SetData]('someData', [1, 2, 3]);
myModel[SetData]('otherData', 42);
Caveats
Because almost everything in JavaScript is an object, it is not possible to have robust type-checking at runtime. For example, strings constructed with theString()
constructor will pass an Object
type check, as will functions and arrays.