{"id":16788555,"url":"https://github.com/mna/implement.js","last_synced_at":"2026-03-17T21:05:39.993Z","repository":{"id":57272961,"uuid":"3845873","full_name":"mna/implement.js","owner":"mna","description":"Strong type-checking for dependency injection and method arguments.","archived":false,"fork":false,"pushed_at":"2014-08-12T01:10:05.000Z","size":221,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T14:35:08.605Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-03-27T16:27:37.000Z","updated_at":"2021-09-22T13:24:12.000Z","dependencies_parsed_at":"2022-09-12T16:12:17.671Z","dependency_job_id":null,"html_url":"https://github.com/mna/implement.js","commit_stats":null,"previous_names":["puerkitobio/implement.js"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/mna/implement.js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mna%2Fimplement.js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mna%2Fimplement.js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mna%2Fimplement.js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mna%2Fimplement.js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mna","download_url":"https://codeload.github.com/mna/implement.js/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mna%2Fimplement.js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30631440,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T17:32:55.572Z","status":"ssl_error","status_checked_at":"2026-03-17T17:32:38.732Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T08:18:14.154Z","updated_at":"2026-03-17T21:05:39.978Z","avatar_url":"https://github.com/mna.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# implement.js #\n\nStrong type-checking for dependency injection and method arguments.\n\n## Usage ##\n\nGiven the dynamic nature of Javascript, when decoupling our modules with some kind of dependency injection, or receiving arguments in a publicly exposed method, we often end up making either bold assumptions about what the object should do (\"yeah, I've got a good feeling this object implements `doThatCrazyThang()`, let's just call it and see...\"), or we litter our code with type checking mechanisms.\n\nThis small library is an attempt to bring the good of strongly typed languages into the good of dynamically typed Javascript in one simple method call. In static languages, dependency injection is usually based on an interface that defines exactly what to expect from the injected instance. That's what the `implements()` method of *implement.js* does. It takes an actual instance, an expected \"interface\", and ensures that the interface is fully implemented by the instance.\n\nLikewise, the `assertArgs()` method takes an array of values (typically, the `arguments` array of the calling  function) and an array of types, and ensures that the values are of the expected types. But there's more to it, keep reading.\n\n### Changelog ###\n\n*   **v0.3.0**: Support for constructor functions, thanks to @micha149.\n*\t**v0.2.2**: Added the `version` property to the public exports, and the missing `UnexpectedTypeError`.\n*\t**v0.2.1**: Added this changelog to the README\n*\t**v0.2.0**: Throw a `UnexpectedTypeError` when calling `assertArgs()` with invalid values, instead of `NotImplementedError`. Replaced the `NotImplementedError.errors.typeMismatch` and `NotImplementedError.errors.missingKeys` with `UnexpectedTypeError.typeMismatch` and `NotImplementedError.missingKeys`. In a DRY exercise, made `NotImplementedError` inherit from `UnexpectedTypeError`, based on this article: http://dustinsenos.com/articles/customErrorsInNode\n*\t**v0.1.1**: Refactored errors implementation based on this article: http://dustinsenos.com/articles/customErrorsInNode\n*\t**v0.1.0**: Initial release.\n\n### Install ###\n\n`npm install implementjs`\n\n### implements() ###\n\nThe `implements()` method expects an actual value (the instance), the expected implementation (the *interface*), and an options hash (more on this later). If something is not implemented, it throws a `NotImplementedError` exception.\n\n*\t**Basic example**: the expected interface can be defined using \"typeof\" strings. That is, 'object', 'function', 'string', 'boolean', 'number', 'undefined':\n\n\t```javascript\n\tvar impl = require(\"implementjs\");\n\n\tmodule.exports = function(externalDependency) {\n\t\t// Check if the dependency implements the expected interface\n\t\timpl.implements(externalDependency, {whistle: 'function', lyrics: 'string', applause: 'boolean'});\n\n\t\t// Return the actual exports...\n\t\treturn {\n\t\t\tdoSomething: function () {}\n\t\t};\n\t}\n\t```\n\n*\t**Strongly typed interface**: the expected interface can be defined using actual values. Their \"typeof\" equivalent will be used, the actual values used are not relevant (code stripped for brevity):\n\n\t```javascript\n\timpl.implements(externalDependency, {whistle: function() {}, lyrics: '', applause: false});\n\t```\n\n*\t**More specific types**: using the values approach, as opposed to the \"typeof strings\" method, makes it possible to define Dates, Arrays and Regular Expressions as \"first-class\" types (using typeof, these types are simply 'object's):\n\n\t```javascript\n\timpl.implements(externalDependency, {whistle: function() {}, \n\t\t\t\t\t\t\t\t\t\tlyrics: '', \n\t\t\t\t\t\t\t\t\t\tapplause: false, \n\t\t\t\t\t\t\t\t\t\tstart: new Date(), \n\t\t\t\t\t\t\t\t\t\tchoir: []});\n\t```\n\n*\t**Using the builder**: syntactic sugar, instead of providing values, you can use the builder helper fields, so that you can build the interface this way:\n\n\t```javascript\n\timpl.implements(externalDependency, {whistle: impl.Function, \n\t\t\t\t\t\t\t\t\t\tlyrics: impl.String, \n\t\t\t\t\t\t\t\t\t\tapplause: impl.Boolean, \n\t\t\t\t\t\t\t\t\t\tstart: impl.Date, \n\t\t\t\t\t\t\t\t\t\tchoir: impl.Array});\n\t```\n\n\tFor completeness' sake, you can define a `null` (impl.Null) or `undefined` (impl.Undefined) key on your interface. And some more syntactic sugar, one-character builder helper fields are available, so this is equivalent:\n\n\t```javascript\n\timpl.implements(externalDependency, {whistle: impl.F, \n\t\t\t\t\t\t\t\t\t\tlyrics: impl.S, \n\t\t\t\t\t\t\t\t\t\tapplause: impl.B, \n\t\t\t\t\t\t\t\t\t\tstart: impl.D, \n\t\t\t\t\t\t\t\t\t\tchoir: impl.A});\n\t```\n\n*\t**Nested objects**: the expected interface can define nested objects. And the type definition can mix and match typeof strings and short and long builder helper fields:\n\n\t```javascript\n\timpl.implements(externalDependency, {whistle: impl.F, \n\t\t\t\t\t\t\t\t\t\tlyrics: impl.S, \n\t\t\t\t\t\t\t\t\t\tapplause: impl.B, \n\t\t\t\t\t\t\t\t\t\tstart: impl.D, \n\t\t\t\t\t\t\t\t\t\tchoir: impl.A,\n\t\t\t\t\t\t\t\t\t\ttour: {\n\t\t\t\t\t\t\t\t\t\t\tcities: impl.A,\n\t\t\t\t\t\t\t\t\t\t\tdates: impl.A,\n\t\t\t\t\t\t\t\t\t\t\tticketPrice: impl.Number,\n\t\t\t\t\t\t\t\t\t\t\tband: {\n\t\t\t\t\t\t\t\t\t\t\t\tdrum: 'string',\n\t\t\t\t\t\t\t\t\t\t\t\tguitar: 'string',\n\t\t\t\t\t\t\t\t\t\t\t\tbass: impl.S\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t```\n\n*\t**Persistent interface**: some interfaces are required by many modules, can be reused, or are just too ugly when defined inline. They can be stored in a separate module, and built using the builder helper methods, which are chainable:\n\n\t```javascript\n\tvar impl = require(\"implementjs\");\n\n\tmodule.exports = impl.createInterface()\n\t\t\t\t\t\t\t.addFunction(\"whistle\")\n\t\t\t\t\t\t\t.addString(\"lyrics\")\n\t\t\t\t\t\t\t.addBoolean(\"applause\")\n\t\t\t\t\t\t\t.addDate(\"start\")\n\t\t\t\t\t\t\t.addArray(\"choir\")\n\t\t\t\t\t\t\t.getInterface();\n\t```\n\n*\t**Persistent nested interface**: the builder helper methods can also be used to create nester interfaces, with the child interface specified as a second argument to `.addObject()`:\n\n\t```javascript\n\tvar impl = require(\"implementjs\");\n\n\tvar intfChild = impl.createInterface()\n\t\t\t\t\t\t\t.addFunction(\"whistle\")\n\t\t\t\t\t\t\t.addString(\"lyrics\")\n\t\t\t\t\t\t\t.addBoolean(\"applause\")\n\t\t\t\t\t\t\t.addDate(\"start\")\n\t\t\t\t\t\t\t.addArray(\"choir\")\n\t\t\t\t\t\t\t.getInterface();\n\tvar intfParent = impl.createInterface()\n\t\t\t\t\t\t\t.addString(\"concert\")\n\t\t\t\t\t\t\t.addObject(\"song\", intfChild)\n\t\t\t\t\t\t\t.getInterface();\n\t```\n\n*\t**Array of expected interfaces**: a single object can be expected to implement more than one interface. This can be verified in one single call, using an array of expected interfaces. Assuming the *intf1* and *intf2* are interfaces required by the module:\n\n\t```javascript\n\timpl.implements(externalDependency, [intf1, intf2]);\n\t```\n\n*\t**Options**: the options hash supports only one option at the moment:\n\n\t*\t*allowNullObjects*: boolean - if true, null is allowed when an object is expected, whether this object is an Array, a Date, a RegExp or a plain Object. Default is false (if null when an object is expected, will throw an error).\n\n    *   *instanciateConstructors*: boolean - if true, and the expected argument to `implements` is a function, it is treated as a constructor and is instanciated. Defaults to true.\n\n### assertArgs() ###\n\nThe `assertArgs()` (or the aliases `assertArguments()` and `assertValues()`) method expects an actual array (the arguments to validate, usually the `arguments` array of the calling function), an array of the expected types, and an options hash. If a value is not of the expected type, it throws a `UnexpectedTypeError` exception.\n\n*\t**Example**: the types can be defined in the same way as the `implements()` method, that is, using typeof strings or builder helper fields - the short or long variety. It will *not* deeply validate objects, you should use `implements()` on this specific value for this. Assuming `impl` is the variable used to require *implementjs*:\n\n\t```javascript\n\tfunction twistAndShout(band, members, duration) {\n\t\timpl.assertArgs(arguments, [impl.S, impl.A, impl.N]);\n\t}\n\t```\n\n*\t**Options**: the options hash supports the following keys:\n\n\t*\t*allowNullObjects*: boolean - if true, null is allowed when an object is expected, whether this object is an Array, a Date, a RegExp or a plain Object. Default is false (if null when an object is expected, will throw an error).\n\t*\t*optionalArgsStartIndex*: number - indicates the index at which the arguments are optional. Defaults to no optional arguments.\n\t*\t*strict*: boolean - if true, a `TooManyArgsError` exception will be thrown if more arguments than expected are provided. Default is false.\n\n*\t**Optional arguments**: if an *optionalArgsStartIndex* is provided, the value at this index will be validated against the expected type at the same index. If it doesn't match, the same value will be validated against the next expected type, until a match is found (or there are no more expected types):\n\n\t```javascript\n\tfunction twistAndShout(band, members, duration, encore, wave) {\n\t\t// If only a String and a Boolean is specified, this is ok. Even if a\n\t\t// String, a Boolean and an (unexpected) Function is specified in the arguments array,\n\t\t// this is OK (because strict mode is off by default)\n\t\timpl.assertArgs(arguments, [impl.S, impl.A, impl.N, impl.B, impl.B], \n\t\t\t\t\t\t{optionalArgsStartIndex: 1});\n\t}\n\t```\n\n*\t**Returns an array**: as an added bonus, the method returns an array with the values positioned at the matching index based on the expected types. This can be useful when there are optional arguments:\n\n\t```javascript\n\tfunction twistAndShout(band, members, duration, encore, wave) {\n\t\t// If only a String and a Boolean is specified\n\t\tvar ar = impl.assertArgs(arguments, [impl.S, impl.A, impl.N, impl.B, impl.B], \n\t\t\t\t\t\t{optionalArgsStartIndex: 1});\n\n\t\t// ar = [StringValue, undefined, undefined, BooleanValue, undefined]\n\t}\n\t```\n\n### Exceptions ###\n\nThree custom error objects are used in *implement.js*:\n\n*\t*UnexpectedTypeError*: has a `typeMismatch` property, which is a hash where the key is the key in error (prefixed if from a nested object), and the value is an object with two properties, `actualType` and `expectedType`.\n*\t*NotImplementedError*: has two properties, `typeMismatch` which is inherited from `UnexpectedTypeError`, and `missingKeys`, which is an array of expected keys missing from the instance (nested keys are prefixed, so if key *leaf* on object *tree* is missing, it will be named *tree.leaf*).\n*\t*TooManyArgsError*: thrown when `assertArgs()` is in strict mode and there are more values then expected. There are no additional properties on this object.\n\nAll errors have `message`, `stack` and `name` properties.\n\n## License ##\n\n(MIT License)\n\nCopyright (C) 2012 Martin Angers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmna%2Fimplement.js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmna%2Fimplement.js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmna%2Fimplement.js/lists"}