Exploring all things software engineering and beyond...

Running ES6 Modules Native in the Browser using TypeScript

ES6 Modules have a clean and intuitive syntax, and it's nice to know that there is now widespread support natively to use them in the browser. The main drawback to date is that not every browser version dating back supports ES6 modules and thus a module/loader bundler is still required for production applications (only one of many reasons). However it can be a lot of work to spin up Webpack or SystemJS just to throw together a small test application. Another use case might be if building an intranet app in a controlled/known environment using a supported browser. Regardless of the reason here is how you can use ES6 modules natively in the browser using TypeScript or JavaScript. (This post will focus on TypeScript, but you can use plain JS too)

1. Create an ES6 module

A module is nothing more than a self-contained set of functionality, executed within their own scope and not on the global scope. We'll use the export keyword to expose that functionality outside the module, and the import keyword to use that exposed functionality in another module.

export class Person {
        return '123 Pine St.';

2. Create another ES6 module importing the module created previously

import { Person } from "./Person.js";
export class ContactInfo{
    getContactInfo() {
        const person = new Person();
        const address = person.getAddress();
        console.log(`The address is: ${address}`)

Make sure to note that "bare" modules are not supported. This restriction allows for browsers to scale in the future when using module loaders, and allow "bare" modules to contain special meaning or functionality. This syntax below is not supported and you'll get the following error in the browser, even though the application might build correctly:

import { Person } from "Person";
"Uncaught TypeError: Failed to resolve module specifier "Person". Relative references must start with either "/", "./", or "../"."

The correct syntax is to reference the exact file directly. 

3. Configure tsconfig.json

Configure the module type to be "ES6." Using anything else will ultimately yield in errors in the browser at runtime as the other module types are not supported. 

"compilerOptions": {
    "module": "es6"

4. Leverage the "module" type in index.html

As this isn't a classic script, we must identify the file as being a module using the type="module" attribute. Note you can use the async attribute with modules if desired which will execute the script as soon as possible, without a guarantee of order, and also not waiting for the HTML parsing to finish. There is also no need to add the defer attribute as it behaves like this by default; a module script can't block the parser.

<script src="scripts/typescript/Person.js" type="module"></script>
<script src="scripts/typescript/ContactInfo.js" type="module"></script>

5. Run and check for errors

If you see any errors such as "exports is not defined" or "define is not defined" then you probably have the tsconfig.json configured incorrectly and it's instead transpiling to another module type that is unsupported in the browser (i.e. commonjs, amd, etc..) natively without a module loader. If testing the basic functionality above, upon calling getContactInfo() you should see the output in the debugger.

For a full list of browser compatibility on ES6 module support, see the following link:
JavaScript modules via script tag

If you're wondering about the .mjs module file extension support in TypeScript, see the following discussion on GitHub: Support '.mjs' output

Buyer Beware: the client has burned us before, put stock in code on the server

Have you ever played poker? Do you like a full house? A royal flush? Those are amazing but the hand you're dealt is two 3's, a 5, a jack, and a 9. Software development and its future, often has speculation laced in but sometimes we need to deal with the hand we're dealt. As primarily a web developer these days and on and off over the last 15+ years, there has always been that "next thing in the pipeline that will save the horrors of web development." Sounds kind of like a gambler that says, "if I can just play one more hand, I'll win it all back and more!"

This all builds up to be a bit cynical, but the reality is web development is and always has been an ever changing, volatile platform for which to develop. There probably is no better group of people accustomed to change than your savvy modern day web developer. I don't think anyone should label web developers as 'in love' with how modern web development is implemented in general. It's a kludge of open source libraries and techniques that defines the very word 'web.' That being a tangled web of various libraries and techniques all pointing back to that love-hate relationship with JavaScript. 

JavaScript in some respects is that mediocre poker hand we were dealt. However in the web world it's perceived weaknesses are at the same time it's strengths. Albeit a dynamic language that frustrates many, it's also flexible and powerful providing us with that "write once run anywhere" ability due to the fact of browsers all being able to interpret and run JavaScript.The language has IMO matured exponentially in the last 5-10 years, and with the addition of high level superset languages like TypeScript, working with JavaScript isn't the nightmare it once was.

However, you won't see any "I love JavaScript" bumper stickers on the back of my car. I spent years working in various languages like WinBatch, VB, VBA, VB.NET, and C# on the server or client. The reality is today those do us no good in web development on the client. Ah but you say, WebAssembly is here to save the day! Yeah I've heard this tune played before (cough, cough, Silverlight). C# running on the client, same business rules on the client and server, our woes are over, etc. etc.. Listen, I know Silverlight and WebAssembly are not the same at all. The only analogy I'm making is it's not the 1st time in history something has been deemed the "savior" to web development. Call WebAssembly 'x' for this purpose, and "project x will save the day!" In reality I'm actually quite optimistic about where WebAssembly is going to take us on the client. Imagine using C# for everything? That would be beautiful. For an optimistic view of WebAssembly and it's future with smart client development, check out Rocky Lhotka's post: A Bright Future for the Smart Client

Let's pump the brakes for a minute though. It's early 2018 and I'm expected to deliver today using the hand I'm dealt. In that case we are looking at a host of JS libraries and frameworks to help us build a responsive SPA when it comes to web development. I also have the option of using server-side heavy tech, but for the purpose of this post, I'll focus on the more mainstream approach these days using JS and building responsive JS apps. The bright side of all this is there isn't that "one way" to do development. I suppose that's a double edged sword, but the reality is it gives us options. Like ES6? Use it. Don't like JS? Use TypeScript? Like Angular? Nope. Use React or Vue. Like LESS? Nope. Use SASS. The options go on and on and on. I do agree in some ways this stinks. I often talk about the days early in my career where being really awesome at VB6 meant you could conquer the world, finish your job, and not have to worry about 1,000 surrounding technologies, languages, and libraries. I feel like times were a bit more straight forward. However back in 2002, it was accepted that everybody and their brother was using Windows + IE and that was it. Times have changed and so has the web. The expectation today is run anywhere. Thankfully, JavaScript has provided that flexible nature to allow use to be fluid and make our way to being platform and browser agnostic. Thus we can reach a lot of people with a single line of code.

So what does this all mean? Well I can say for one thing today: the web is a volatile space and likely to continue beings so in the near future. Even if WebAssembly or any other technology comes along and truly revolutionizes web development as promised, it will take a long time to wash out the gabillion lines of JS out there running the web today. That and the fact we'll probably be in a scenario where, "1/5th of the APIs are available with new tech 'x', and a roadmap is available for the remainder." Point being, set yourself up well to play this hand you're dealt today no matter how it plays out. Be smart and architect your application properly to be successful today and in the future no matter what technologies throw their hat in the ring to save web development. This includes the next best JS framework ever to be head and shoulders above them all!

Put stock in your code on the server

How do we play our hand to isolate ripple effects as much as possible as technology proves to be volatile? The answer might be in looking at statistics from the past, rather than looking into a crystal ball. 

Let's take a walk through history for a moment in web development starting 18 years ago in 2000. If you were to build say a banking or financial web application beginning at that time and continuing to keep current on a mix of .NET and client technologies, here is the journey you would might have taken:

Classic ASP -> ASP.NET Webforms -> ASP.NET Webforms + AJAX Control Toolkit -> ASP.NET Webforms + Silverlight -> ASP.NET MVC (aspx engine) -> ASP.NET MVC (razor engine) -> AngularJS -> Angular 2,3,4,5....

What does this all mean? The web layer is and always has been volatile. Maybe the buck stops with WebAssembly in the future, but that is still yet to be proven; that's another crystal ball moment. I want to use the history of the journey we've taken to help make sound decisions today. This leads me to the point that I'd take little stock in the front-end of our application, and put stock in the server-side code.

Here's an example. Back in 2002, you wrote that financial or banking application using C#. If you at the time had used sound OO techniques dating back to the 70's, unit tested the code, and wrapped it in abstractions to deliver the data, that code could be mostly intact today. I couldn't say that for probably a single line of code on the front-end. 

I'm not oblivious to the changes in both C# and .NET technologies, so odds are code was probably refactored. However the most volatile part on the server is that being the manner in accessing the data and the manner in delivering the data. Sure there is volatility there as well. Maybe those core classes and business logic were once deliver via .NET Remoting, then an ASMX Web Service, followed by WCF, than WebAPI. However those should be ultra-thin layers acting as a means to an end to deliver the data. Same applies to accessing the data. You started with ADO.NET, then to LINQ to SQL, then to Entity Framework and through all of its versions. Again, this didn't necessarily need to change all of your core code; this is another layer that should be understood as being volatile.

So back to the web, today. I want to view the front-end layer as a thin and volatile layer that as history has shown has about an average of a 1-3 year lifespan it seems. I want the meat and potatoes of my code that matters on the server. I won't even get into the intellectual property or security considerations of why code should be on the server as I think that should be obvious with today's tech stack. I want to leverage the front-end code for only front-end matters. I want a pristine viewmodel of sorts returned via a service, where the only logic happening in the presentation layer is that of view manipulation and presentation logic. I've been on enough projects where IP and rules heavy work was done on the client (why, because you can!), only to have year long projects to refactor that code back to the server behind services.

Using an oversimplified view of the layers, let's look at the following using physical size to demonstrate placement and emphasis of code. Instead of a heavy client-side implementation like shown below:

I believe the implementation should be more along the lines of this based on everything discussed:

Put stock in your code on the server. History shows that in the battle of volatility the server-side wins out and the web is just too dynamic. Add to this security, IP considerations, and the unbelievably volatile and ever-changing world that is the 'web' it really is the smart move to make. The cynics will bring up leveraging CPUs on the client vs the server, but the world has changed. Data is small and efficient. The server has oodles of horsepower and is a known entity; we control it. The client is a wildcard; we don't know what they have. Long gone are the days where we know the client is a Windows machine controlled by an organization with a spec machine.

When analyzing data, as opposed to fat SOAP payloads of the past, payloads now are concise and to the point leveraging HTTP standards to deliver data as lean as possible. One must also yield to the fact that the days of 4GLTE and blazing fast WiFi and networking speeds are becoming the norm and will only continue to get better. It's pointless to argue that all apps must work flawlessly offline because that isn't reality across the board. We're in a world where if we aren't connected, things just don't work. Don't plan on making any airline reservations or bank account transfers when your device has no connectivity. I'll also avoid edge cases at this point as I understand there could be web apps made for 3rd world countries or the like where bandwidth is a premium, so data across the wire must be under the microscope. I'm sticking with mainstream, modern day, web development here. I'll leave the door open for you to evaluate edge cases that don't fit the 80/20 rule and call for an exception.

I can always go to the server and scrape off the services layer and re-introduce a new tech to replace that abstraction if needed. There are architectural recommendations within the server as well to make sure to thin-out the services facade and keep lightweight as that too as mentioned is know to be volatile over time. On the front-end, I want this same flexibility. Odds are I'll be asked to rewrite my web app in a few years to 'project x' tech which is the best thing ever for the web! I need to be lean up top and be able to scrape that thin veneer or icing off the cake, and put a new layer on easily. This isn't so easy to do if I've put the majority of my code on the client. When the boss or architect comes to me and says, "we are redoing our financial app using new front-end tech" I'll be positioned to say, "no problem, we can mitigate the ripple affect because the key logic and inner-workings of our app are stable and on the server, so we'll just need to redo the thin veneer that is the web-client code."

Strict Property Initialization Checks in TypeScript 2.7

The upcoming version 2.7 release of TypeScript has a new compiler option available named "strictPropertyInitialization" This when set to true causes the compiler to ensure all properties in class instances are initialized. If they are not, a compiler error will be generated upon building for each uninitialized property.

As an aside, to get the most current version of TypeScript installed that hasn't yet been officially released you can run the following npm command:

npm install -g typescript@next

This installs the most recent nightly build which will allow me to have access to the new compiler option:

+ typescript@2.7.0-dev.20180113

If you are beginning a new project, wait to initialize your TypeScript project until after the newest version is installed, so you can easily see all the new compiler options. You can also add them manually to your existing tsconfig.json file. Upon initializing a fresh tsconfig file (tsc --init), you will see the new strictPropertyInitialization check:

/* Enable strict checking of property initialization in classes.*/
"strictPropertyInitialization": true, 

To test this, let's use the current simple TypeScript class:

class Person {
    firstName: string;
    lastName: string;
    address1: string;
    address2: number;

With "strictPropertyInitialization":true set, the following compiler errors will be emitted upon building:

error TS2564: Property 'firstName' has no initializer and is not definitely assigned in the constructor.
error TS2564: Property 'lastName' has no initializer and is not definitely assigned in the constructor.
error TS2564: Property 'address1' has no initializer and is not definitely assigned in the constructor.
error TS2564: Property 'address2' has no initializer and is not definitely assigned in the constructor.

This is exactly what we would expect. One way to satisfy the check we can do is mark the parameter as optional using the ? as we might with address2. This will remove the error related with this field, as undefined is acceptable:

class Person {
    firstName: string;
    lastName: string;
    address1: string;
    address2?: number;  //Optional field, type includes undefined

The build now only generates 3 errors:

error TS2564: Property 'firstName' has no initializer and is not definitely assigned in the constructor.
error TS2564: Property 'lastName' has no initializer and is not definitely assigned in the constructor.
error TS2564: Property 'address1' has no initializer and is not definitely assigned in the constructor.

To satisfy the strict property initialization for the rest of the class, we can initialize the remaining properties and we will get a successful build:

class Person {
    firstName: string = "Allen";
    lastName: string = "Conway";
    address1: string = "123 Main St";
    address2?: number;

There is also a way if needed to individually suppress the property initialization checks on an individual basis. This can be done by including a definite assignment assertion using a ! mark immediately after the property name. This will allow the following to build successfully:

class Person {
    firstName: string = "Allen";
    lastName: string = "Conway";
    address1!: string;  // Suppress strict initialization check
    address2?: number;

Visual Studio Code Error with TypeScript Project: "Option 'project' cannot be mixed with source files on a command line"

If you create a TypeScript project in Visual Studio code in a path that contains spaces, you might end up getting the following error after configuring your build task and building the application:
> Executing task: tsc -p "c:\Projects\Test Harness Applications\TypeScript\MyTypeScriptApp\tsconfig.json" <
error TS5042: Option 'project' cannot be mixed with source files on a command line.
The terminal process terminated with exit code: 1
Notice the path I used to create the application contained spaces: 
"c:\Projects\Test Harness Applications\TypeScript\MyTypeScriptApp"

After a little digging I found this GitHub open issue: https://github.com/Microsoft/vscode/issues/32400. The good news is according to the information within that link, this is in process to be fixed in an upcoming version of Visual Studio Code (currently 2/2018). In the interim the solution is just to make sure the full path to your application does not contain any spaces. I verified that by doing this, the compiler issue will go away, and the project build successfully.