Interfaces with stampit 2 - javascript

As mentioned in the interface section of 'Programming javascript applications' you can implement interfaces with stampit. When reducing the unnecessary stuff from the example, I end up with something like this:
const fooBarInterface =
stampit()
.methods(
{
foo: () => {
throw new Error('connect not implemented');
},
bar: () => {
throw new Error('save not implemented');
}
}
)
Excerpt from an interface definition:
If your class claims to implement an interface, all methods defined by
that interface must appear in its source code before the class will
successfully compile.
So now putting the interface in use
const fooBarImplementation =
stampit()
.compose(fooBarInterface)
.methods(
{
foo: () => {
// implement me
}
}
}
)
Now, when composing an object from the stamp, there should be an error because bar is not implemented by fooBarImplementation. It isn't, and I fear that it's pretty hard to get something like this in place because there is no compiling at all.
So, my question is: Am I doing it wrong or is this half baked thing what Eric Elliott calls an 'Interface'?

The module you've created is great! You really know stampit well.
Although, in JavaScript I would recommend to go a different path. Namely, check if method is present.
if (obj.bar) kangaroo.bar();
And remove the fooBarInterface entirely. But if you need to check the method presence while creating the object, then you should do it similar to your module.
var ValidateFooBar = stampit()
.init(function() {
if (!_.isFunction(this.foo)) throw new Error('foo not implemented');
if (!_.isFunction(this.bar)) throw new Error('bar not implemented');
});
And use it:
const fooBarImplementation = stampit()
.compose(ValidateFooBar)
.methods({
foo: function() {
// implement me
}
});
Will throw: Error: bar not implemented

Related

JavaScript ES6 circular require references

I've moved a bunch of data onto the UI represented by classes. The different classes all map to each other using FKs. I'm trying to keep references to those data classes to only 1, to avoid the confusing pointers, so I keep the FKs as Ints on the related classes, then look them up when I need to. Originally I had no Cache class, but I tried to create a pipe for the data between the UI and data, and funneled all the cached-data requests through its own class (which I thought would help with the circular references too) but no dice unfortunately.
I sandboxed the below example to try to simplify my issue. I used static methods to simplify the example. Also the sandbox has more-easily hierarchical classes, unlike the app. The example's probably overly-complicated but I'm trying my best to show why I (think I) need the circular references. This might be an issue with webpack, or maybe a philosophical one where I shouldn't have circular references, any feedback would be incredible! (I think the same scenario would break node.js as well)
Cache.js
const Person = require("./Person.js")
const Group = require("./Group.js")
class Cache {
static set() {
Person.set([
{ name: "Sean", groupID: 1 },
{ name: "Greg", groupID: 1 }
])
Group.set([ {id: 1, name: "A"} ])
}
static getGroupByID(id) {
return Group.getByID(id)
}
static getPeopleInGroup(id) {
return Person.getInGroup(id)
}
static getFirstPerson() {
return Person.getFirstPerson()
}
}
module.exports = Cache
/* use closures?
module.exports = {
set: () => Cache.set()
getGroupByID: (id) => Cache.getGroupByID(id),
getPeopleInGroup: (id) => Cache.getPeopleInGroup(id),
getFirstPerson: () => Cache.getFirstPerson()
}
*/
Person.js
const Cache = require("./Cache.js")
const list = []
class Person {
static set(people) {
people.forEach( p => {
list.push( new Person(p) )
})
}
static getInGroup(id) {
const ar = []
list.forEach( p => {
if (p.data.GroupID == id)
ar.push(p)
})
return ar
}
static getFirstPerson() {
return list[0]
}
constructor(data) {
this.data = data
}
sayHello() {
const group = Cache.getGroupByID(this.data.groupID)
const groupName = group.data.name
console.log(`${this.data.name} from ${groupName}`)
}
}
module.exports = Person
Group.js
const Cache = require("./Cache.js")
const hash = {}
class Group {
static set(groups) {
groups.forEach( g => {
hash[g.ID] = new Group(g)
})
}
static getGroupByID(id) {
return hash[id]
}
constructor(data) {
this.data = data
}
sayPeople() {
const ppl = Cache.getPeopleInGroup(this.data.ID)
console.log(`${this.data.name} has ${ppl.join(", ")}`)
}
}
module.exports = Group
app.js
const Cache = require("./Cache.js")
Cache.set()
let person = Cache.getFirstPerson()
person.sayHello()
let group = Cache.getGroupByID(person.data.groupID)
group.sayPeople()
In this example, the first bad reference is:
Class Person { ...
sayHello() {
const group = Cache.getGroupByID(this.data.groupID)
(Cache is an empty object)
So, cache.js requires person.js and then person.js requires cache.js. That is a circular require that gives you the problem you see.
The design issue here is that person.js checks cache.js for data (that seems reasonable), but you've made methods in cache.js that require knowledge of specific data types from the cache. That creates your circular dependency.
The usual generic answer to these circular dependencies is to figure out what code is needed by both modules and break that out into it's own module where that module does not depend upon anything.
It isn't quite obvious to me how you would do that in this case. Instead, the problem seems to be caused because you made statics in the cache like getGroupByID() that have NOTHING to do with the cache at all and are creating these circular dependencies. That seems to be the core problem. If you remove those from cache.js, then that will free up cache.js to just do its job generically and you can remove the two require() statements causing the circular dependency from them.
Then, the question comes where to put those statics. These seem to be a bit of design problem. You're apparently trying to have person.js and group.js be peers that don't know anything about one another. But, you have methods where that isn't really true. sayHello() in person.js ends up calling into group.js and sayPeople() in group.js ends up calling into person.js.
I would suggest that you need to make a new common base class for group.js and person.js that contains shared implementation and perhaps that's where you can put those statics. I don't exactly understand what the underlying code is trying to do to know for sure that this is the best solution, but the idea is that you make person.js depend upon its base class rather than person.js depend upon group.js, thus breaking the circular dependencies.

Subclassing in Javascript

I'm trying to make a subclass of an image library on github called Jimp. As far as I can tell from the docs, you don't instantiate the class in the usual way. Instead of saying new Jimp(), it seems the class has a static method called read that acts as a constructor. From the docs...
Jimp.read("./path/to/image.jpg").then(function (image) {
// do stuff with the image
}).catch(function (err) {
// handle an exception
});
It looks like from the docs, that that image returned by read() is an instance allowing the caller to do stuff like image.resize( w, h[, mode] ); and so on.
I'd like to allow my subclass callers to begin with a different static method that reads an image and does a bunch of stuff, summarized as follows...
class MyJimpSubclass extends Jimp {
static makeAnImageAndDoSomeStuff(params) {
let image = null;
// read in a blank image and change it
return Jimp.read("./lib/base.png").then(_image => {
console.log(`image is ${_image}`);
image = _image;
let foo = image.bar(); // PROBLEM!
// ...
// ...
.then(() => image);
}
bar() {
// an instance method I wish to add to the subclass
}
// caller
MyJimpSubclass.makeAnImageAndDoSomeStuff(params).then(image => {
//...
});
You might be able to guess that nodejs gets angry on the line let foo = image.bar();, saying
TypeError image.bar is not a function
.
I think this is understandable, because I got that image using Jimp.read(). Of course that won't return an instance of my subclass.
First idea: Change it to MyJimpSubclass.read(). Same problem.
Second idea: Implement my own static read method. Same problem.
static read(params) {
return super.read(params);
}
Third idea: Ask SO
The implementation of Jimp.read refers to Jimp specifically, so you would have to copy and change it in your subclass (ick, but not going to break anything since the constructor is also part of the API) or make a pull request to have it changed to this and have subclassing explicitly supported:
static read(src) {
return new Promise((resolve, reject) => {
void new this(src, (err, image) => {
if (err) reject(err);
else resolve(image);
});
});
}
Alternatively, you could just implement all your functionality as a set of functions on a module. This would be next on my list after making a pull request. Would not recommend a proxy.
const makeAnImageAndDoSomeStuff = (params) =>
Jimp.read("./lib/base.png").then(image => {
console.log(`image is ${image}`);
let foo = bar(image);
// …
return image;
});
function bar(image) {
// …
}
module.exports = {
makeAnImageAndDoSomeStuff,
bar,
};
Even changing the prototype would be better than a proxy (but this is just a worse version of the first option, reimplementing read):
static read(src) {
return super.read(src)
.then(image => {
Object.setPrototypeOf(image, this.prototype);
return image;
});
}
You have a couple of options. The cleanest is probably to make a subclass like you started, but then implement the Jimp static method on it, as well as your own. In this case, it's not really inheritance, so don't use extends.
class MyJimp {
static read(...args) {
return Jimp.read.apply(Jimp, args);
}
static makeAnImage(params) {
return this.read(params)
.then(image => {
// do stuff
return image
});
}
}
From there, I would make an object which has all of the new functions you want to apply to image:
const JimpImageExtension = {
bar: () => { /* do something */ }
};
Finally, in your static methods, get the image and use Object.assign() to apply your new functions to it:
class MyJimp {
static read(...args) {
return Jimp.read.apply(Jimp, args)
.then(image => Object.assign(image, JimpImageExtension));
}
static makeAnImage(params) {
return this.read(params)
.then(image => {
// do stuff
image.bar();
return image;
});
}
}
This should do the trick by applying your extra functions to the image. You just need to make sure that you apply it at every point that can generate an image (if there is more than just read). Since in the other functions, it's using your version of read(), you only need to add the functions in the one.
Another approach would be if Jimp makes their image class accessible, you could also add them to the prototype of that (though usually in libraries like this, that class is frequently inaccessible or not actually a class at all).
This might be a way to do it. Start with your own read method, and have it change the prototype of the returned object.
static read(...params) {
return super.read(...params).then(image) {
image.prototype = MyJimpSubclass;
resolve(image);
}
}

Mock Es6 classes using Jest

I'm trying to mock an ES6 class with a constructor that receives parameters, and then mock different class functions on the class to continue with testing, using Jest.
Problem is I can't find any documents on how to approach this problem. I've already seen this post, but it doesn't resolve my problem, because the OP in fact didn't even need to mock the class! The other answer in that post also doesn't elaborate at all, doesn't point to any documentation online and will not lead to reproduceable knowledge, since it's just a block of code.
So say I have the following class:
//socket.js;
module.exports = class Socket extends EventEmitter {
constructor(id, password) {
super();
this.id = id;
this.password = password;
this.state = constants.socket.INITIALIZING;
}
connect() {
// Well this connects and so on...
}
};
//__tests__/socket.js
jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');
expect(Socket).toHaveBeenCalledTimes(1);
socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');
As obvious, the way I'm trying to mock Socket and the class function connect on it is wrong, but I can't find the right way to do so.
Please explain, in your answer, the logical steps you make to mock this and why each of them is necessary + provide external links to Jest official docs if possible!
Thanks for the help!
Update:
All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."
Full disclosure: I wrote it. :-)
The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.
Call jest.mock('./mocked-class.js');, and also import './mocked-class.js'.
For any class methods you want to track calls to, create a variable that points to a mock function, like this: const mockedMethod = jest.fn();. Use those in the next step.
Call MockedClass.mockImplementation(). Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2).
The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling jest.fn().mockImplementation(), with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on.
The same thing can be done by calling jest.mock('mocked-class.js', factoryFunction), where factoryFunction is again the same argument passed in 3 and 4 above.
An example is worth a thousand words, so here's the code.
Also, there's a repo demonstrating all of this, here:
https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working
First, for your code
if you were to add the following setup code, your tests should pass:
const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want
Socket.mockImplementation(() => {
return {
connect: connectMock
};
});
(Note, in your code: Socket.mock.calls[0][1] should be [0][0], and [0][2] should be [0][1]. )
Next, a contrived example
with some explanation inline.
mocked-class.js. Note, this code is never called during the test.
export default class MockedClass {
constructor() {
console.log('Constructed');
}
mockedMethod() {
console.log('Called mockedMethod');
}
}
mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.
import MockedClass from './mocked-class';
export default class MockedClassConsumer {
constructor() {
this.mockedClassInstance = new MockedClass('yo');
this.mockedClassInstance.mockedMethod('bro');
}
}
mocked-class-consumer.test.js - the test:
import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';
jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.
// console.log(MockedClass()); // logs 'undefined'
let mockedClassConsumer;
const mockedMethodImpl = jest.fn();
beforeAll(() => {
MockedClass.mockImplementation(() => {
// Replace the class-creation method with this mock version.
return {
mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
};
});
});
beforeEach(() => {
MockedClass.mockClear();
mockedMethodImpl.mockClear();
});
it('The MockedClassConsumer instance can be created', () => {
const mockedClassConsumer = new MockedClassConsumer();
// console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
expect(mockedClassConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
const mockedClassConsumer = new MockedClassConsumer();
expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});
it('We can check if the consumer called a method on the class instance', () => {
const mockedClassConsumer = new MockedClassConsumer();
expect(mockedMethodImpl).toHaveBeenCalledWith('bro');
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});
For me this kind of Replacing Real Class with mocked one worked.
// Content of real.test.ts
jest.mock("../RealClass", () => {
const mockedModule = jest.requireActual(
"../test/__mocks__/RealClass"
);
return {
...mockedModule,
};
});
var codeTest = require("../real");
it("test-real", async () => {
let result = await codeTest.handler();
expect(result).toMatch(/mocked.thing/);
});
// Content of real.ts
import {RealClass} from "../RealClass";
export const handler = {
let rc = new RealClass({doing:'something'});
return rc.realMethod("myWord");
}
// Content of ../RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "The.real.deal "+input;
}
// Content of ../test/__mocks__/RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "mocked.thing "+input;
}
Sorry if I misspelled something, but I'm writing it on the fly.

Using switchMap with Promises

I am learning Angular2, following the "Tour of Heroes" tutorial on Angular.io. Near the end of the tutorial, we set up routing to a detail page and pass a parameter indicating the hero to displace. This is handled using the params Observable in ActivatedRoute. We use switchMap to redirect from the params Observable to a Promise to return the data we actually want based on the parameter.
The syntax used in the tutorial is concise, so I tried to break it out into building blocks to get a better understanding of what is going on. Specifically, I have tried to replace right arrow notation with an actual function, that I think is identical. But my modification does not work.
Here is the code:
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
//.switchMap(this.getHero)
.subscribe(hero => this.hero = hero);
}
getHero(params: Params) : Promise<Hero> {
return this.heroService.getHero(+params['id']);
}
What confuses me is why using the line that is currently commented out instead of the line above it, I get an error: "Cannot read property 'getHero' of undefined." The two versions of code look identical to me.
Fat-arrow function preserves the context of execution, allowing the this "variable" to be the same as in the parent scope. If you use .switchMap(this.getHero) then this will point to something else, not the component.
getHero(params: Params) : Promise<Hero> {
// "this" is not what you expect here
return this.heroService.getHero(+params['id']);
}
So this.heroService is undefined here.
You'd need to bind your getHero function.
.switchMap(this.getHero.bind(this))
Otherwise your change is identical. Using bind like this allows you to pass getHero as a standalone function to switchMap without losing what this means to it.
You can experiment with it:
'use strict';
const foo = {
bar: 'baz',
getBar: function() {
return this.bar;
}
};
foo.getBar();
// => 'baz'
const myGetBar = foo.getBar;
myGetBar();
// => TypeError: Cannot read property 'bar' of undefined
const boundGetBar = foo.getBar.bind(foo);
boundGetBar();
// => 'baz'
const otherObj = { bar: 'hi' };
const otherBoundGetBar = foo.getBar.bind(otherObj);
otherboundGetBar();
// => 'hi'
otherObj.getBar = myGetBar;
otherObj.getBar();
// => 'hi'
You cannot use this.getHero like in your snippet because
it's undefined (the service returns Observable that you have to subscribe before using its data)
it's not a property (doesn't have get modifyer).

Does JavaScript have the interface type (such as Java's 'interface')?

I'm learning how to make OOP with JavaScript. Does it have the interface concept (such as Java's interface)?
So I would be able to create a listener...
There's no notion of "this class must have these functions" (that is, no interfaces per se), because:
JavaScript inheritance is based on objects, not classes. That's not a big deal until you realize:
JavaScript is an extremely dynamically typed language -- you can create an object with the proper methods, which would make it conform to the interface, and then undefine all the stuff that made it conform. It'd be so easy to subvert the type system -- even accidentally! -- that it wouldn't be worth it to try and make a type system in the first place.
Instead, JavaScript uses what's called duck typing. (If it walks like a duck, and quacks like a duck, as far as JS cares, it's a duck.) If your object has quack(), walk(), and fly() methods, code can use it wherever it expects an object that can walk, quack, and fly, without requiring the implementation of some "Duckable" interface. The interface is exactly the set of functions that the code uses (and the return values from those functions), and with duck typing, you get that for free.
Now, that's not to say your code won't fail halfway through, if you try to call some_dog.quack(); you'll get a TypeError. Frankly, if you're telling dogs to quack, you have slightly bigger problems; duck typing works best when you keep all your ducks in a row, so to speak, and aren't letting dogs and ducks mingle together unless you're treating them as generic animals. In other words, even though the interface is fluid, it's still there; it's often an error to pass a dog to code that expects it to quack and fly in the first place.
But if you're sure you're doing the right thing, you can work around the quacking-dog problem by testing for the existence of a particular method before trying to use it. Something like
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
So you can check for all the methods you can use before you use them. The syntax is kind of ugly, though. There's a slightly prettier way:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
This is standard JavaScript, so it should work in any JS interpreter worth using. It has the added benefit of reading like English.
For modern browsers (that is, pretty much any browser other than IE 6-8), there's even a way to keep the property from showing up in for...in:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
The problem is that IE7 objects don't have .defineProperty at all, and in IE8, it allegedly only works on host objects (that is, DOM elements and such). If compatibility is an issue, you can't use .defineProperty. (I won't even mention IE6, because it's rather irrelevant anymore outside of China.)
Another issue is that some coding styles like to assume that everyone writes bad code, and prohibit modifying Object.prototype in case someone wants to blindly use for...in. If you care about that, or are using (IMO broken) code that does, try a slightly different version:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
Pick up a copy of 'JavaScript design patterns' by Dustin Diaz. There's a few chapters dedicated to implementing JavaScript interfaces through Duck Typing. It's a nice read as well. But no, there's no language native implementation of an interface, you have to Duck Type.
// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
var i = 1, methodName;
while((methodName = arguments[i++])){
if(typeof obj[methodName] != 'function') {
return false;
}
}
return true;
}
// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
// IT'S A DUCK, do your duck thang
}
JavaScript (ECMAScript edition 3) has an implements reserved word saved up for future use. I think this is intended exactly for this purpose, however, in a rush to get the specification out the door they didn't have time to define what to do with it, so, at the present time, browsers don't do anything besides let it sit there and occasionally complain if you try to use it for something.
It is possible and indeed easy enough to create your own Object.implement(Interface) method with logic that baulks whenever a particular set of properties/functions are not implemented in a given object.
I wrote an article on object-orientation where use my own notation as follows:
// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
constructor: function(name) {
Dog.superClass.call(this, name);
},
bark: function() {
alert('woof');
}
}).implement(Mammal);
There are many ways to skin this particular cat, but this is the logic I used for my own Interface implementation. I find I prefer this approach, and it is easy to read and use (as you can see above). It does mean adding an 'implement' method to Function.prototype which some people may have a problem with, but I find it works beautifully.
Function.prototype.implement = function() {
// Loop through each interface passed in and then check
// that its members are implemented in the context object (this).
for(var i = 0; i < arguments.length; i++) {
// .. Check member's logic ..
}
// Remember to return the class being tested
return this;
}
JavaScript Interfaces:
Though JavaScript does not have the interface type, it is often times needed. For reasons relating to JavaScript's dynamic nature and use of Prototypical-Inheritance, it is difficult to ensure consistent interfaces across classes -- however, it is possible to do so; and frequently emulated.
At this point, there are handfuls of particular ways to emulate Interfaces in JavaScript; variance on approaches usually satisfies some needs, while others are left unaddressed. Often times, the most robust approach is overly cumbersome and stymies the implementor (developer).
Here is an approach to Interfaces / Abstract Classes that is not very cumbersome, is explicative, keeps implementations inside of Abstractions to a minimum, and leaves enough room for dynamic or custom methodologies:
function resolvePrecept(interfaceName) {
var interfaceName = interfaceName;
return function curry(value) {
/* throw new Error(interfaceName + ' requires an implementation for ...'); */
console.warn('%s requires an implementation for ...', interfaceName);
return value;
};
}
var iAbstractClass = function AbstractClass() {
var defaultTo = resolvePrecept('iAbstractClass');
this.datum1 = this.datum1 || defaultTo(new Number());
this.datum2 = this.datum2 || defaultTo(new String());
this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
this.method2 = this.method2 || defaultTo(new Function('return new Object();'));
};
var ConcreteImplementation = function ConcreteImplementation() {
this.datum1 = 1;
this.datum2 = 'str';
this.method1 = function method1() {
return true;
};
this.method2 = function method2() {
return {};
};
//Applies Interface (Implement iAbstractClass Interface)
iAbstractClass.apply(this); // .call / .apply after precept definitions
};
Participants
Precept Resolver
The resolvePrecept function is a utility & helper function to use inside of your Abstract Class. Its job is to allow for customized implementation-handling of encapsulated Precepts (data & behavior). It can throw errors or warn -- AND -- assign a default value to the Implementor class.
iAbstractClass
The iAbstractClass defines the interface to be used. Its approach entails a tacit agreement with its Implementor class. This interface assigns each precept to the same exact precept namespace -- OR -- to whatever the Precept Resolver function returns. However, the tacit agreement resolves to a context -- a provision of Implementor.
Implementor
The Implementor simply 'agrees' with an Interface (iAbstractClass in this case) and applies it by the use of Constructor-Hijacking: iAbstractClass.apply(this). By defining the data & behavior above, and then hijacking the Interface's constructor -- passing Implementor's context to the Interface constructor -- we can ensure that Implementor's overrides will be added, and that Interface will explicate warnings and default values.
This is a very non-cumbersome approach which has served my team & I very well for the course of time and different projects. However, it does have some caveats & drawbacks.
Drawbacks
Though this helps implement consistency throughout your software to a significant degree, it does not implement true interfaces -- but emulates them. Though definitions, defaults, and warnings or errors are explicated, the explication of use is enforced & asserted by the developer (as with much of JavaScript development).
This is seemingly the best approach to "Interfaces in JavaScript", however, I would love to see the following resolved:
Assertions of return types
Assertions of signatures
Freeze objects from delete actions
Assertions of anything else prevalent or needed in the specificity of the JavaScript community
That said, I hope this helps you as much as it has my team and I.
Hope, that anyone who's still looking for an answer finds it helpful.
You can try out using a Proxy (It's standard since ECMAScript 2015): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
latLngLiteral = new Proxy({},{
set: function(obj, prop, val) {
//only these two properties can be set
if(['lng','lat'].indexOf(prop) == -1) {
throw new ReferenceError('Key must be "lat" or "lng"!');
}
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(prop == 'lat' && !(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
//longitude is in range between 0 and 180
else if(prop == 'lng' && !(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
obj[prop] = val;
return true;
}
});
Then you can easily say:
myMap = {}
myMap.position = latLngLiteral;
If you want to check via instanceof (asked by #Kamaffeather), you can wrap it in an object like so:
class LatLngLiteral {
constructor(props)
{
this.proxy = new Proxy(this, {
set: function(obj, prop, val) {
//only these two properties can be set
if(['lng','lat'].indexOf(prop) == -1) {
throw new ReferenceError('Key must be "lat" or "lng"!');
}
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(prop == 'lat' && !(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
//longitude is in range between 0 and 180
else if(prop == 'lng' && !(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
obj[prop] = val;
return true;
}
})
return this.proxy
}
}
This can be done without using Proxy but instead the classes getters and setters:
class LatLngLiteral {
#latitude;
#longitude;
get lat()
{
return this.#latitude;
}
get lng()
{
return this.#longitude;
}
set lat(val)
{
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(!(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
this.#latitude = val
}
set lng(val)
{
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//longitude is in range between 0 and 180
if(!(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
this.#longitude = val
}
}
abstract interface like this
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
create an instance:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
and use it
let x = new MyType()
x.print()
You need interfaces in Java since it is statically typed and the contract between classes should be known during compilation. In JavaScript it is different. JavaScript is dynamically typed; it means that when you get the object you can just check if it has a specific method and call it.
When you want to use a transcompiler, then you could give TypeScript a try. It supports draft ECMA features (in the proposal, interfaces are called "protocols") similar to what languages like coffeescript or babel do.
In TypeScript your interface can look like:
interface IMyInterface {
id: number; // TypeScript types are lowercase
name: string;
callback: (key: string; value: any; array: string[]) => void;
type: "test" | "notATest"; // so called "union type"
}
What you can't do:
Define RegExp patterns for type value
Define validation like string length
Number ranges
etc.
there is no native interfaces in JavaScript,
there are several ways to simulate an interface. i have written a package that does it
you can see the implantation here
Try this: Describe the interface as a class and use #implements JSDoc to show that a given class implements the interface defined. You'll see red squiggly lines on the class name if its not implementing some properties. I tested with VSCode.
// #ts-check
// describe interface using a class
class PlainInterface {
size = 4;
describe() {}
show(){ }
}
/**
* #implements PlainInterface
*/
class ConcretePlain {
size = 4;
describe() {
console.log('I am described')
}
show(){
console.log('I am shown')
}
}
const conc = new ConcretePlain();
conc.describe();
Javascript does not have interfaces. But it can be duck-typed, an example can be found here:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
This is an old question, nevertheless this topic never ceases to bug me.
As many of the answers here and across the web focus on "enforcing" the interface, I'd like to suggest an alternative view:
I feel the lack of interfaces the most when I'm using multiple classes that behave similarly (i.e. implement an interface).
For example, I have an Email Generator that expects to receive Email Sections Factories, that "know" how to generate the sections' content and HTML. Hence, they all need to have some sort of getContent(id) and getHtml(content) methods.
The closest pattern to interfaces (albeit it's still a workaround) I could think of is using a class that'll get 2 arguments, which will define the 2 interface methods.
The main challenge with this pattern is that the methods either have to be static, or to get as argument the instance itself, in order to access its properties. However there are cases in which I find this trade-off worth the hassle.
class Filterable {
constructor(data, { filter, toString }) {
this.data = data;
this.filter = filter;
this.toString = toString;
// You can also enforce here an Iterable interface, for example,
// which feels much more natural than having an external check
}
}
const evenNumbersList = new Filterable(
[1, 2, 3, 4, 5, 6], {
filter: (lst) => {
const evenElements = lst.data.filter(x => x % 2 === 0);
lst.data = evenElements;
},
toString: lst => `< ${lst.data.toString()} >`,
}
);
console.log('The whole list: ', evenNumbersList.toString(evenNumbersList));
evenNumbersList.filter(evenNumbersList);
console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));
With an interface you can implement a way of polymorphism. Javascript does NOT need the interface type to handle this and other interface stuff. Why? Javascript is a dynamically typed language. Take as example an array of classes that have the same methods:
Circle()
Square()
Triangle()
If you want to know how polymorphism works the Book MFC of David Kruglinsky is great (written for C++)
Implement in those classes the method draw() push the instances of those classes in the array and call the draw() methods in a loop that iterates the array. That's completely valid. You could say you implemented implicitly an abstract class. Its not there in reality but in your mind you did it and Javascript has no problem with it. The difference with an real interface is that you HAVE to implement all the interface methods and that's in this case not needed.
An interface is a contract. You will have to implement all the methods. Only by making it statically you have to do that.
Its questionable to change a language like Javascript from dynamic to static. Its not mend to be static. Experienced developers have no problems with the dynamic nature of Javascript.
So the reason to use Typescript are not clear to me. If you use NodeJS together with Javascript you can build extremely efficient and cost effective enterprise websites. The Javascript/NodeJS/MongoDB combination are already great winners.
I know this is an old one, but I've recently found myself needing more and more to have a handy API for checking objects against interfaces. So I wrote this: https://github.com/tomhicks/methodical
It's also available via NPM: npm install methodical
It basically does everything suggested above, with some options for being a bit more strict, and all without having to do loads of if (typeof x.method === 'function') boilerplate.
Hopefully someone finds it useful.
This is old but I implemented interfaces to use on ES6 without transpiller.
https://github.com/jkutianski/ES6-Interfaces
It bugged me too to find a solution to mimic interfaces with the lower impacts possible.
One solution could be to make a tool :
/**
#parameter {Array|object} required : method name list or members types by their name
#constructor
*/
let Interface=function(required){
this.obj=0;
if(required instanceof Array){
this.obj={};
required.forEach(r=>this.obj[r]='function');
}else if(typeof(required)==='object'){
this.obj=required;
}else {
throw('Interface invalid parameter required = '+required);
}
};
/** check constructor instance
#parameter {object} scope : instance to check.
#parameter {boolean} [strict] : if true -> throw an error if errors ar found.
#constructor
*/
Interface.prototype.check=function(scope,strict){
let err=[],type,res={};
for(let k in this.obj){
type=typeof(scope[k]);
if(type!==this.obj[k]){
err.push({
key:k,
type:this.obj[k],
inputType:type,
msg:type==='undefined'?'missing element':'bad element type "'+type+'"'
});
}
}
res.success=!err.length;
if(err.length){
res.msg='Class bad structure :';
res.errors=err;
if(strict){
let stk = new Error().stack.split('\n');
stk.shift();
throw(['',res.msg,
res.errors.map(e=>'- {'+e.type+'} '+e.key+' : '+e.msg).join('\n'),
'','at :\n\t'+stk.join('\n\t')
].join('\n'));
}
}
return res;
};
Exemple of use :
// create interface tool
let dataInterface=new Interface(['toData','fromData']);
// abstract constructor
let AbstractData=function(){
dataInterface.check(this,1);// check extended element
};
// extended constructor
let DataXY=function(){
AbstractData.apply(this,[]);
this.xy=[0,0];
};
DataXY.prototype.toData=function(){
return [this.xy[0],this.xy[1]];
};
// should throw an error because 'fromData' is missing
let dx=new DataXY();
With classes
class AbstractData{
constructor(){
dataInterface.check(this,1);
}
}
class DataXY extends AbstractData{
constructor(){
super();
this.xy=[0,0];
}
toData(){
return [this.xy[0],this.xy[1]];
}
}
It's still a bit performance consumming and require dependancy to the Interface class, but can be of use for debug or open api.
Js doesn't have interfaces but typescript does!
While there isn't a interface in javaScript as there is in Java you could mimic the behaviour a bit with the code under this message. because an interface is basicly an enforced contract you could build it yourself.
The code below exists out of 3 classes an interface, parent and child class.
The Interface has the methods to check if the methods and properties exist required exist.
The Parent is used to enforce the required methods and properties in the child using the Interface class.
The Child is the class that the parents rules are enforced on.
After you set it up correctly you will see an error in the console if a method or property is missing in the child and nothing if the child implements the contract correctly.
class Interface {
checkRequiredMethods(methodNames) {
setTimeout( () => {
const loopLength = methodNames.length;
let i = 0
for (i; i<loopLength; i++) {
if (typeof this[methodNames[i]] === "undefined") {
this.throwMissingMethod(methodNames[i]);
}
else if (typeof this[methodNames[i]] !== "function") {
this.throwNotAMethod(methodNames[i]);
}
}
}, 0);
}
checkRequiredProperties(propNames) {
setTimeout( () => {
const loopLength = propNames.length;
let i = 0
for (i; i<loopLength; i++) {
if (typeof this[propNames[i]] === "undefined") {
this.throwMissingProperty(propNames[i]);
}
else if (typeof this[propNames[i]] === "function") {
this.throwPropertyIsMethod(propNames[i]);
}
}
}, 0);
}
throwMissingMethod(methodName) {
throw new Error(`error method ${methodName} is undefined`);
}
throwNotAMethod(methodName) {
throw new Error(`error method ${methodName} is not a method`);
}
throwMissingProperty(propName) {
throw new Error(`error property ${propName} is not defined`);
}
throwPropertyIsMethod(propName) {
throw new Error(`error property ${propName} is a method`);
}
}
class Parent extends Interface {
constructor() {
super()
this.checkRequiredProperties([
"p1",
"p2",
"p3",
"p4",
"p5"
]);
this.checkRequiredMethods([
"m1",
"m2",
"m3",
"m4"
]);
}
}
class Child extends Parent {
p1 = 0;
p2 = "";
p3 = false;
p4 = [];
p5 = {};
constructor() {
super();
}
m1() {}
m2() {}
m3() {}
m4() {}
}
new Child()
No, but it has mixins.
You can use Abstract sub-classss or mixins as an alternative https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#mix-ins

Categories