If you're writing code in TypeScript then I'll assume that you've put a focus on writing code efficiently by leveraging static typing and identifying potential bugs at compile time instead of dealing with them later on in development.
Hey, Tyler here. I'm currently working on some great web development and digital marketing products and services over at ZeroToDigital.
If that sounds interesting to you, please check it out!
The single responsibility principle should then be on your radar as well. It states that
a computer programming principle that states that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class, module or function. All its services should be narrowly aligned with that responsibility. (Wikipedia).
You could implement this principle by creating and using classes and functions within the same file, but then our coding efficiency would eventually be lost when trying to scroll through a long file. Its best to create individual files that hold our modules and include them when necessary.
There are many ways to do this (depending on your setup) including export, import,include, require, and others. However, these might not work if you're writing front-end TypeScript code to be used in the browser.
In this post, I'm going to share with you how to write modular code leveraging TypeScript Namespaces that will be compiled into a single file and used in the browser.
You might not need this exact setup, but I'll be using Node.js with the TypeScript NPM module. I'll also be leveraging TypeScript's AMD module.
Here's my tsconfig.json file:
JSON
{
"compilerOptions": {
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"module": "amd",
"outFile": "public/js/app.js"
},
"include": [
"dev/ts/**/*.ts",
"dev/ts/*.ts"
]
}
Having declared everything in the tsconfig file, I can initiate the compilation by simply running tsc in the terminal.
In this particular instance, we want to create a class called Alert that will allow us to close an alert popup and perform an Ajax call to initialize a cookie in Node.js.
TypeScript
export class Alert {
closeBtn: HTMLElement
container: HTMLElement
url: string
constructor (closeBtn: HTMLElement, container: HTMLElement, url: string) {
this.closeBtn = closeBtn
this.container = container
this.url = url
}
}
To start, we'll declare our class and the 3 include properties:
Then we'll set their valued with the constructor() function.
Next, we need to add an event listener to the closeBtn, if it exists.
TypeScript
export class Alert {
...
constructor (closeBtn: HTMLElement, container: HTMLElement, url: string) {
...
// If element exists, add event listener
if (typeof(this.closeBtn) != 'undefined' && this.closeBtn != null) {
this.closeBtn.addEventListener("click", (e: Event) => this.setCookie())
}
}
setCookie() { ... }
}
We'll perform a check that the element exists and then add a 'click' event listener that, when clicked, we'll call our setCookie() function.
Here's what that function will do:
TypeScript
...
setCookie() {
let container = this.container
let url = this.url
$.ajax({
url: url,
success: function(data){
container.style.display = 'none'
}
})
}
}
Here, we're setting container and url equal to the properties of the class because the this keyword's lexical environment will be altered when inside of the Ajax call.
Inside the Ajax call we're performing a GET request with the URL and hiding the container if the call is successful.
If you've created a browser eventListener before using vanilla JavaScript then this should be mostly recognizable. The main difference is that we want to export the class, not just declare it.
Where do TypeScript's Namespaces come in? We're going to put that entire class inside of a namespace called clickEvents:
TypeScript
namespace clickEvents {
export class Alert {
...
}
}
Namespaces, previously called internal modules, allow us to group functionalities together logically.
Here, we're calling our namespace clickEvents because we may decide to add other click-type events other than just alerts.
Now we can use this namespace and all included functionality in our front-end scripts.
TypeScript
/// <reference path="namespaces/clickEvents.ts" />
new clickEvents.Alert(
<HTMLElement>document.querySelector(`.alert-close`),
<HTMLElement>document.querySelector(`.alert`),
'/sessions/alerts'
)
We'll include our namespace by using a triple-slash directive to declare this file's dependency on the 'clickEvents.ts' file. Then we'll create a new alert.
To do this, we first need to use the name of our namespace and then the class within the namespace. Then we'll include our arguments that the class will use as the closeBtn, container and url.
Now you just need to run tsc in your terminal to compile the files and test them in the browser.
Linux
tsc