npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

a-proper-interface

v2.3.0

Published

A more proper and native interface system for javascript than what is available

Readme

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