Strong Type or Not

I enjoy writing javascript without depending on strong type definitions, but if you can somehow understand the parameter expectaction of a function, than it is realy helpful during development, even that function accepts any type of parameter(s).

Most javascript editors are capable of showing coding information, while you are writing. Even though, vi (vim) is my favorite, default vim is lacking of high level productivity tools. Currently, I am using Visual Studio Code (VSCode), which is working on all well know OS and has great addons like code highlighting, code snippets and intellisense.

On the other hand, having a good ide is not always enough, if the javascript code written as npm package or module does not contain a good type definition file. So while developing NodeJS and use well know packages, I can navigate to source code by clicking F12 on VSCode. Than curiosity take over the job and I started to investigate what is the best practive of doing this.

Today, I will try to describe how to make your type definition for your module and how to publish that module as an npm package. I saw some tools out there to generate the type definition file, but they look inefficient and why do we need another tool for such a straight forward work anyway.

Type Definition

I will create a package and put a type definition for the module defined in the package. This package is already live on npmjs so you can also install and try.

The package secure-callback is accomplishing a very simple task. Checks the validity of a callback method provided. By means of validity, it checks if the callback is provided and than executes the callback method. Even it is simple, when working with callbacks this is done all the time. Let’s take a look at the code and go back to our main topic; defining of types.

Below is the module defined with Typescript. I will not go into details of Typescript, but if curious about it checkout this link.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function isFunction(object) {
return typeof (object) === 'function';
}
class SecureCallback {
throwException: Boolean;
notFunctionMsg: string;
callbackRequiredMsg: string;
constructor(throwException: Boolean = false, notFunctionMsg: string = null, callbackRequiredMsg: string = null) {
this.throwException = throwException ? throwException : false;
this.notFunctionMsg = notFunctionMsg ? notFunctionMsg : 'callback is not a function.';
this.callbackRequiredMsg = callbackRequiredMsg ? callbackRequiredMsg : 'callback should be defined.';
}
respond(callback: Function, ...args) {
if (callback) {
if (isFunction(callback)) {
callback.apply(this, args);
} else if (this.throwException) {
throw new Error(this.notFunctionMsg);
}
} else if (this.throwException) {
throw new Error(this.callbackRequiredMsg);
}
}
respondsuccess(callback: Function, successMsg: string = null, ...args) {
this.respond(callback, null, successMsg, args);
}
responderror(callback: Function, errorMsg: string = null, ...args) {
this.respond(callback, errorMsg, args);
}
}
export = SecureCallback;

Basically there is one constructor and 3 methods for this module. To be short, respond checks the callback and applies the arguments for it. In the constructor you can define if respond method should throw exception and custom messages for invalid callbacks. I can leave the typescript implementation as it is and generate .js file for this, but someone who uses this npm package cannot see the definitions when writing its code in an editor that is capable of showing method signatures. To accomplish this, we will define a simple type definitions file. Type definition files are like [filename].d.ts. Before going into details here is the type definition file for our SecureCallback module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Type definitions for [secure-callback] [1.1.7]
// Project: [Secure Callback]
// Definitions by: [volem] <[nodejs.withvolkan.com]>
/// <reference types="typescript" />
/*~ This declaration specifies that the class constructor function
*~ is the exported object from the file
*/
declare class SecureCallback {
constructor(throwException?: Boolean, notFunctionMsg?: string, callbackRequiredMsg?: string);
respond(callback: Function, ...args): void;
respondsuccess(callback: Function, successMsg?: string, ...args): void;
responderror(callback: Function, errorMsg?: string, ...args): void;
}
export module SecureCallback{}

If you are familiar with JAVA or .NET, this looks like an interface definition of them. Interfaces are skeletons for class implementations and they are used as types.

Here we provide the signature of our class for type definition of it. It is pretty straight forward and mostly a copy paste from your typescript source code. The only difference I have to mention here is: Type signature cannot contain default values as Typescript source. You can see the difference in our constructor of SecureCallback

1
2
3
4
// Typescript source
constructor(throwException: Boolean = false, notFunctionMsg: string = null, callbackRequiredMsg: string = null)
// Types definition file
constructor(throwException?: Boolean, notFunctionMsg?: string, callbackRequiredMsg?: string);

To tell that it is an optional parameter, we use “?” in our types definition file. Save this file together with your source of your module. In this case, I store my generated .js file for typescript in “/dist” folder. - I will not go into details for how to generate .js file from .ts, maybe on another post -

So my dist folder contains;

  • securecallback.js
  • securecallback.d.ts
    By doing so we are nearly there. We have to also tell this to npm that we have a types definition file. Tp do so, update your package.json file like below;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "name": "secure-callback",
    "version": "1.1.7",
    "description": "Checks callback functions validity",
    "main": "./dist/securecallback.js",
    "types": "./dist/securecallback.d.ts",
    // ... Rest of our package.json
    }

Publish NPM Package

Now, our package is ready for publish. Publishing npm package is pretty straight forward. I was planning to make another post for it, but it does not deserve since it is very easy to do so. You need to have a npm account prior to start publishing. If you don’t have still please go here and create one. After your account is in place open your terminal and;

1
npm login

And then all you have to do is to run;

1
npm publish

Here are the 3 things you need to note for your publish;

  1. Package name should not be taken by someone else. (Publish fails)
  2. You can publish the directory which has package.json file in it. (Publish fails)
  3. All files and directories will be published unless they are ignored by .gitignore file.

If you would like to update your package it is also pretty straight forward. There are 3 different types of updates => “Major, Minor and Patch”

  • Major : Increases the major field of your version. ie. From 1.0.0 to 2.0.0
  • Minor : Increases the major field of your version. ie. From 1.0.0 to 1.1.0
  • Patch : Increases the patch field of your version. ie. From 1.0.0 to 1.1.0
    After this you can again publish your package.
1
2
npm version [major|minor|patch]
npm publish

I think that is all folks. Thanks for reading.

See the source used in this post from github