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
TypeScript and Electron, the Right Way!
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

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.

Leave a Reply

10 Comments on "TypeScript and Electron The Right Way"

Notify of
avatar
Sort by:   newest | oldest | most voted
Michael Jenner
Guest

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
Guest

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”?

Dave Bush
Guest

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.

Eduardo Brites
Guest

I had to run “npm install typings –global” , otherwise I’d got a “‘typings’ is not recognized as an internal or external command”

Michael Jenner
Guest

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

Michael Jenner
Guest

What would you recommend for controlling state of the application?

YvesSchelpe
Guest

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

Dave Bush
Guest

Yes, typings has changed since I first wrote the article

Deilan
Guest

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

Dave Bush
Guest

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

wpDiscuz