Let us begin with our next day, a recap:
Check out our previous blog for part 1

  • Compiling TS files with TSC
  • Setting up tsconfig.json
  • Structuring our code to output everything to build folder

It is good to know the above, but not very helpful when you start a real project. So let us code something today and serve a file that makes our work visible via an HTTP server. Also before diving deep into something, I love the ability to include files so that I can organize my code properly. Thus let us set up the ExpressJS server and play with importing files.


Setting up Http Server – ExpressJS

Let me install ExpressJS with the simplest standard way we all know:

npm install express --save

Further, as it goes without saying let us create server.ts that will have our server code. With ES6 I use to import express with a simple import as:

import express from "express";

But unfortunately, that does not work with typescript for CommonJS/AMD/UMD modules, the proper way to import a CommonJS module is as below:

import express = require("express");

I also came across the following:

import * as express from "express";

but apparently, that is not the correct way to do it because import * as creates an identifier that is a module object, emphasis on an object. According to the ES6 spec, this object is never callable or newable – it only has properties. Attempting to use import * as express and then invoking express() is always illegal according to the ES6 spec. In some runtime+transpilation environments, this might happen to work anyway, but it might break at any point in the future without warning, which will make you sad.

We will understan the imports in more details after we set up the HTTP server ;)

Setting up the standard “Hello, World!” example by official ExpressJS docs in our server.js

import express = require("express");

const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello, World!'));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

and now running from the project root terminal:

./node_modules/.bin/tsc && node build/server.js

Everything works great and accessing http://localhost:3000 I can see the glorious “Hello, World!”

But his is not the end, there is more to the above example, we can specify types for ExpressJS in TS project as following and can actually use the benefit of TypeScript.

npm i @types/express --save-dev

and thus this gives the developer a great autocomplete feature and exposing the methods & properties available, which is great:

and thus making our server.js more typed as below:

import express = require("express");

const app: express.Application = express();
const port: number = 3000;
app.get('/', (req: express.Request, res: express.Response): express.Response => {
	return res.send('Hello, World!');
});
app.listen(port, (): void => console.log(`Example app listening on port ${port}!`));

I can see typescript is really really simple and figured out the below myself for functions:

function (arg1: <type>, arg2: <type>): <function-return-type> {
  // return something of <function-return-type>
}

And string, number, boolean & void are very general types mostly available in any programming language, but for express.Application, express.Request & express.Response types, the autocomplete helped me!

The above is really great.

I already have a feeling that I am going to use TypeScript the rest of my life!

Back to our HTTP server, we really don’t want to server “Hello, World!” but we would like our TS compiled javascript to be executed on the home page, so I updated the server.ts to following:

import express = require("express");
const path = require("path");

const app: express.Application = express();
const port: number = 3000;

app.use("/static", express.static(path.join(__dirname, '..', 'build')));
app.get('/', (req: express.Request, res: express.Response): express.Response => {
	return res.send(`<!DOCTYPE html>
		<html>
			<head>
				<title>My experiments with TypeScript</title>
				<style>
				  html,body {
				  	margin:0;
				  	padding:0;
				  	font-family: monospace;
				  	font-size: 20px;
				  }
				  #logger {
				  	background-color: #eee; 
				  	padding: 20px; 
				  	max-width: 100%; 
				  	height: 600px; 
				  	max-height: calc( 90vh - 2em - 35px); 
				  	margin: 10px;
				  	overflow-y: auto;
				  }
				  .log-entry {
				  	max-width: 100%;
				  	padding: 5px;
				  	background-color: #f6f6f6;
				  }
				  .title {
				  	margin: 10px;
				  }
				</style>
			</head>
			<body>
				<h1 class="title">Logger:</h1>
				<div id="logger"></div>
				<script src="/static/index.js" async></script>
			</body>
		</html>
	`);
});
app.listen(port, (): void => console.log(`Example app listening on port ${port}!`));

I have done nothing special here, just sending HTML with a bit of styling and have asked express to server static files from build folder. I know it is not ideal right now as server.js can also be served from the static path, but this is just temporary and for learning.

Updating the index.ts file

We have a simple logger output that is being served via express and can include our code from compiled index.js, so let us update index.ts to the following, giving it the ability to log content inside the logger:

let str: string = "Hello, World!";
function log(s: any): void {
	const logger: HTMLElement = document.getElementById("logger");
	const logWrapper: HTMLElement = document.createElement("div");
	logWrapper.className = "log-entry";
	logWrapper.innerHTML = `${(new Date).toLocaleTimeString()}: ${JSON.stringify(s)}`;
	logger.appendChild(logWrapper);
}
log(str);

You may test your server by running the following command:

./node_modules/.bin/tsc && node build/server.js

You should see the following screen if you visit http://localhost:3000


Pretty sleek right!

Just one last thing and we are almost done with our most simplistic ExpressJS setup with TypeScript.
I really do not want to restart the server whenever I update the code base, thus adding nodemon to our repo. Also, I would not want to compile the code again and again so would start the TS compiler with --watch option enabled, start server with nodemon and update the start script to execute both with concurrently node package

npm i nodemon concurrently --save

And updating our start script to:

"scripts": {
    "start": "tsc && concurrently -n \"TypeScript,Nodemon\" \"npm run ts-watch\" \"npm run server-watch\"",
    "ts-watch": "tsc -w",
    "server-watch": "nodemon --watch build/server.js build/server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }

I guess that should do be already and would be helpful for our learning and research further!

Before moving on to Types and other details, I would like to learn more about imports and compatibility with CommonJS, AMD, UMD & ES6 etc, so will focus more on that tomorrow!

The code base of this episode, Part 2, is available at:
https://github.com/Atyantik/typescript-series/tree/part-2

UPDATE:

Next part where we get started with Modules & Namespaces is now available with the title Modules – understanding imports in TypeScript – Part 3.1 – The novice programmer