Home » Javascript » TypeScript and Electron The Right Way

TypeScript and Electron The Right Way

I’ve been working on a new project over the last weeks that involves getting TypeScript and Electron working together.  Unfortunately, the amount of information available on how to do this correctly is pathetically none existent.

That isn’t to say there aren’t sites that don’t try.  But in the end, all they show you is how to write essentially the same JavaScript code you would have written using JavaScript with a little bit of type information thrown in.  What I was looking for was something a little more object oriented.  Let’s use ALL of TypeScript.

And then there is the whole setup of TypeScript in Node thing.  This was mostly my not knowing my editor well.  But while we are documenting how to get a TypeScript/Electron app setup, let’s cover that as well.

TypeScript and Electron The Right Way
Photo credit: Smithsonian Institution via Visual hunt / No known copyright restrictions

TypeScript and Electron Project Setup

I’m using Node 6.3.1 and I use WebStorm as my editor (for the record).  But most of what I am about to explain will work regardless of which editor you are using.

The first thing you want to do is to initialize your project using

Package.JSON

npm init

Just walk through the prompts and answer the questions.  There isn’t much in the initialization process that will make any difference to what we are going to do, we just need the resulting package.json file to store the package references we are going to install.

Next, install electron. You’ll want to do this globally and as part of your DEV environment.

npm install -g electron
npm link electron

Typings

Next install typings

npm install –save-dev typings

Typings is what is going to provide the type verification in our typescript files.

To make it all work, we’ll need a typings.json file in the root of our project.  Mine looks like this:

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "github-electron": "registry:dt/github-electron#1.3.3+20160822205855",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:env/node#6.0.0+20160825160117"
  }
}

But what is more important is how I created that file.

To find a typing to install you run the following command:

typings search packageName

Once you’ve done that, you’ll get a list of packages that match.  You’ll need to find the appropriate package to install and the source.  Then, run the following command.

typings install source~package --save --global

For example, if we wanted to install core-js from the dt source, the command would look like:

typings install dt~core-js --save --global

To install the typings that are in your typings.json file, just run

typings install

So, to get the types installed for this project, create a typings.json file in the root of your project and type the typings command above.

TypeScript

Next, you’ll need to setup TypeScript.  For this, you’ll need a tsconfig.json file in your root.  I’ll just give you the file you’ll need here:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "suppressImplicitAnyIndexErrors": true
  },
  "exclude": [
    "node_modules"
  ]
}

The thing that drove me crazy for several hours was that you also need to configure your editor to see this.  I’ll leave that step for you because it will depend on which editor you are using.

The Electron Shell with TypeScript

Now comes the fun part.  The normal code for creating the shell for an Electron application in JavaScript normally looks something like this:

const electron = require('electron');
// Module to control application life.
const app = electron.app;  

// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;  

// Keep a global reference of the window object, 
// if you don't, the window will be closed automatically
// when the JavaScript object is garbage collected.
var mainWindow = null;
// Quit when all windows are closed.
app.on('window-all-closed', function() {
    // On OS X it is common for applications and their
    // menu barto stay active until the user quits 
    // explicitly with Cmd + Q
    if (process.platform != 'darwin') {
        app.quit();
    }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
    // Create the browser window.
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // and load the index.html of the app.
    mainWindow.loadURL('file://' + __dirname + '/index.html');

    // Open the DevTools.
    // mainWindow.webContents.openDevTools();

    // Emitted when the window is closed.
    mainWindow.on('closed', function() {
        // Dereference the window object, usually you 
        // would store windows in an array if your 
        // app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null;
    });
});

 

The trick we want to perform is to create a TypeScript class that does essentially the same thing but using TypeScript syntax.

As you can see from the code above, there are a lot of variables that we are hanging on to.  And since this is a desktop application, like most desktop applications, our entry class is going to need a static entry method.  In fact, to have to work reliably, the whole class is going to need to be static.

To do this, we start by creating a Main class.  Each of the event handlers will be a static function that is initialized when we call the main method.  And just to keep things clean we are going to inject the Electron stuff into the class via the main() method rather than having the class create those dependencies.  This will make everything more testable.

The class we end up with looks like this:

import {BrowserWindow} from 'electron';
export default class Main {
    static mainWindow: Electron.BrowserWindow;
    static application: Electron.App;
    static BrowserWindow;
    private static onWindowAllClosed() {
        if (process.platform !== 'darwin')
            Main.application.quit();
    }
    private static onClose(){
        // Dereference the window object.
        Main.mainWindow = null;
    }
    private static onReady(){
        Main.mainWindow = 
            new Main.BrowserWindow({width: 800, height: 600})
        Main.mainWindow
            .loadURL('file://' + __dirname + '/index.html');
        Main.mainWindow.on('closed', Main.onClose);
    }
    static main(
        app: Electron.App,
        browserWindow: typeof BrowserWindow){
        // we pass the Electron.App object and the 
        // Electron.BrowserWindow into this function
        // so this class has no dependencies.  This
        // makes the code easier to write tests for

        Main.BrowserWindow = browserWindow;
        Main.application = app;
        Main.application.on('window-all-closed',
            Main.onWindowAllClosed);
        Main.application.on('ready', 
            Main.onReady);
    }
}

To finish this up, we create an App.ts file that calls this class.

import { app,BrowserWindow } from 'electron';
import Main from './Main';

Main.main(app,BrowserWindow);

 

Compiling to JavaScript

There is one final step that I almost forgot because my IDE does this for me.  But, you’ll need to compile the TypeScript into JavaScript.

The command to do this is tsc

If you have an earlier version of TypeScript that doesn’t know how to look for the tsconfig.json file, you’ll need to clean up your system so that the correct TSC command runs.

And now we run using electron passing App.js as the file to run.

If you are looking for a project that is already setup and ready to go, you can grab this branch of my new project:
https://github.com/DaveMBush/LinkFilter/tree/Electron-Shell-TypeScript

Did I miss a step?  Let me know in the comments below.

 

Other post in Javascript
Summary
Article Name
TypeScript and Electron, the Right Way!
Description
If you are looking for how to use TypeScript and Electron together using all of the features you'd expect to have from TypeScript, your search ends here.
Author
DMB Consulting, LLC

Related Post

  • Amazing Angular2 DOM Tips, Tricks, and WarningsAmazing Angular2 DOM Tips, Tricks, and Warnings I’ve been working with Angular2 now since RC0 and I’ve learned quite a few things about Angular2 DOM tips, tricks, and warnings that you’ll want to pay attention to as you get […]
  • Unit Testing an Angular 2 CLI ProjectUnit Testing an Angular 2 CLI Project This week we want to continue our series about Angular 2 by looking at the Unit Testing capabilities that Angular 2 provides for us.  What we want to cover today is: Tweaking Karma to […]
  • JavaScript Unit Test Code Coverage Using NodeJSJavaScript Unit Test Code Coverage Using NodeJS A couple of weeks ago, I showed how to get Node.JS and Gulp working with Visual Studio 2015.  Last week I showed you how to bundle, minify, and cache-bust using Gulp.  This week, we are […]
  • You Can Start Using Node TodayYou Can Start Using Node Today I was just getting started writing an article about using Node/JavaScript to drive my Selenium tests and as I was writing the “Prerequisite” section, I realized I have never written the […]
  • Why does JavaScript loop only use last value?Why does JavaScript loop only use last value? You see variations of the question, “Why does JavaScript loop only use the last value?” on StackOverflow all the time.  At work, the guy that sits next to me just ran into the same issue.  […]

About Dave Bush

Dave Bush is a Full Stack ASP.NET developer focusing on ASP.NET, C#, Node.js, JavaScript, HTML, CSS, BootStrap, and Angular.JS. Does your team need additional help in any of the above? Contact Dave today.

  • Michael Jenner

    Great blog! I enjoy reading your articles!

    I tried above on a windows 7 pc, and found that I needed to add:
    npm install -g typescript
    To download the tsc compiler.

    I guess you missed the step of how to run the application.

    Kind regards,
    Michael

    • Michael Jenner

      Ahh … actually the run command is there:
      “electron App.js”

      Question:
      How do you determine what typescript packages to include?

      I noted you used “github-electron”. Why this instead of “electron”?

      • There are simply so may ways you could setup your environment, I’m bound to miss something for someone. One would assume that if you are here reading about how to setup electron with typescript, you probably already have typescript installed, or know how to install it.

        If I remember correctly, github-electron is the more recent packaging of electron.

  • Michael Jenner

    It would be great to see an extra article on typescript/electron where you create a binary for distribution?

  • Michael Jenner

    What would you recommend for controlling state of the application?

  • YvesSchelpe

    Great article, love it. Thanks a lot for this starter/help and insight on how one could build a “boot”/”startup” for electron (with TS).

    I just wanted to add though that today typings might work differenlt when using typescript.
    One command: ‘npm i @types/’ , e.g. ‘npm i @types/electron –save-dev’

    See the TypeScript Handbook on Consumption: http://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html

    • Yes, typings has changed since I first wrote the article

  • Deilan

    Thank you for the great article! One question: what about debugging? For instance in Visual Studio Code.

    • I use WebStorm. But I would imagine just like WebStorm, debugging works about the same as it would for a JavaScript application.