ts-simple-interfaces
v0.4.5
Published
This library attempts to define a set of simplified interfaces for typescript that are compatible with common tools in the ecosystem.
Downloads
3
Readme
Simple Interfaces for Typescript
NOTE: This library is experimental right now and in active alpha development. While you may feel free to use it, it is expected to change considerably.
NOTE: As of v0.5.0 this project is now mantained by Wymp and the
package is published on github. Please use @wymp/ts-simple-interfaces
for future versions of
this package. (This will require setting up an .npmrc
file at some level with the following line:
@wymp:registry=https://npm.pkg.github.com/wymp
.)
TL;DR
The "Simple" suite of packages should be considered a collection of interfaces (defined in this
package) along with a collection of concrete implementations (defined in other packages prefaced
with simple-
) that allow you to write code that is highly portable and highly testable.
The idea is that your code should always use the simple interfaces, for function argument types, and that you should then pass in the concrete implementations that you wish to use.
For example, your application may require an HTTP client, like Axios or Request Promise Native.
To set your application up, you would type the variable holding the client as
SimpleHttpClientInterface
and then set it to a concrete implementation, such as
SimpleHttpClientAxios
from the
simple-http-client-axios
package:
// src/MyClient.ts
import { SimpleHttpClientInterface } from "ts-simple-interfaces";
import { SomeData } from "./Types";
export class MyClient {
public constructor(protected httpClient: SimpleHttpClientInterface) { }
public getSomeData() {
return this.httpClient.request<SomeData>({
//...
});
}
}
// --------------------------------------------------------------------
// src/index.ts
import { MyClient } from "./MyClient";
import { SimpleHttpClientAxios } from "simple-http-client-axios";
const axios = new SimpleHttpClientAxios();
const myClient = new MyClient(axios);
const data = myClient.getSomeData();
// ....
Doing this means that if for any reason in the future it Axios goes out of production or becomes problematic, you can easily switch to a different implementation with minimal adjustments.
Note that the idea is not necessarily that there be no adjustments. Individual implementations have varying features that you may want to use. However, sticking to Simple-conformant implementations not only minimizes the adjustments you have to make when migrating, but it also increases your options for testing, as other people can focus on building robust, widely applicable test implementations that conform to the general Simple interfaces and can be dropped in with no modifications, or at least only minor ones.
Longer Overview
This library attempts to define a set of simplified interfaces for typescript that are compatible with common tools in the ecosystem. Most of these tools are I/O-based, such as pub/subs (amqp), relational databases (mysql, postgres), key-value stores (redis), and loggers (winston), but technically any complex tool is fair game for a simplified interface.
The problem is that many of these tools have unnecessarily complex native interfaces that make
them problematic for testing and portability. For example, Winston is a wonderful logger, but
its LoggerInterface
defines a bunch of methods that are not very useful to the average user,
and furthermore extends NodeJSStream.Transport
, yielding a final interface that defines over
40 methods that have little to do with logging messages. Thus, incorporating these interfaces
into your code unnecessarily expands the contracts that your code uses, making your applications
less flexible, more difficult to test, and more difficult to migrate to new technologies.
Another issue is that Winston's LoggerInterface
is defined in the library itself, meaning that
if you want to use that LoggerInterface
, you have to depend on the whole Winston package. This
isn't technically a huge deal, but it also tends to hamper the responsible specification of
general interfaces that are independent of any single project.
TOC for Simple Suite of Packages
As mentioned in the TL;DR, the "simple" concept is actually a collection of repositories/packages. For each interface defined, the goal is to create one or more "official" concrete implementations based on current popular libraries.
Because the Simple suite is still very much a work in progress, very few concrete implementations have been defined. However, as the interfaces stabilize and are used in production environments, the hope is to deliver more. Feel free to let me know about any concrete implementations that spring up in the wild....
Following is a list of implementations by interface:
SimpleHttpClientInterface
SimpleHttpRequestHandlerInterface
SimpleLoggerInterface
SimplePubSubInterface
SimpleSqlDbInterface
Testing
One of the advantages of referencing these interfaces in your code is that you can use potentially powerful pre-built testing mocks/spies/stubs that conform to them. At the time of this writing, these are being developed here.
For example, you can instantiate a SimpleMockSqlDb
that implements SimpleSqlDbInterface
and
gives you the ability to easily analyze query requests and define responses.
Usage
To use this library, just list it as a dependency[1] and then define classes that implement these interfaces. For example:
// src/MyDatasource.ts
import { SimpleDatasourceInterface } from "ts-simple-interfaces";
export class MyDatasource implements SimpleDatasourceInterface {
....
}
API
The best way to understand the API is by just looking at the declarations file here.
Footnotes
[1] Note that this library should be included as a proper dependency, not a dev dependency, because it may define enums that actually compile down to javascript that your project uses at runtime.