Electron Quick Start with TypeScript

Electron is an open - source framework developed by GitHub that allows you to build cross - platform desktop applications using web technologies such as HTML, CSS, and JavaScript. TypeScript, on the other hand, is a superset of JavaScript that adds static typing to the language, making it more robust and maintainable, especially for larger projects. Combining Electron with TypeScript can significantly enhance the development experience and the quality of your desktop applications. This blog post will guide you through a quick start with Electron using TypeScript, covering fundamental concepts, usage methods, common practices, and best practices.

Table of Contents

  1. [Fundamental Concepts](#fundamental - concepts)
  2. [Setting up the Project](#setting - up - the - project)
  3. [Usage Methods](#usage - methods)
  4. [Common Practices](#common - practices)
  5. [Best Practices](#best - practices)
  6. Conclusion
  7. References

Fundamental Concepts

Electron Basics

  • Main Process: The main process in an Electron application is responsible for creating and managing browser windows. It runs a single instance of Node.js and can use all Node.js APIs.
  • Renderer Process: Each browser window in an Electron application runs in its own renderer process. The renderer process is similar to a traditional web page and can access web APIs like the DOM.
  • Inter - Process Communication (IPC): Since the main process and renderer processes are separate, Electron provides mechanisms for them to communicate with each other, such as ipcMain in the main process and ipcRenderer in the renderer process.

TypeScript Basics

  • Static Typing: TypeScript allows you to define types for variables, functions, and objects. This helps catch type - related errors at compile - time rather than at runtime.
  • Interfaces and Classes: You can use interfaces to define the shape of objects and classes to create reusable object - oriented code.
  • Transpilation: TypeScript code needs to be transpiled into JavaScript before it can be run in the browser or Node.js. Tools like tsc (TypeScript compiler) are used for this purpose.

Setting up the Project

Step 1: Initialize a new project

Create a new directory for your project and initialize a new Node.js project using npm.

mkdir electron - typescript - app
cd electron - typescript - app
npm init -y

Step 2: Install dependencies

Install Electron and TypeScript as development dependencies.

npm install electron typescript --save - dev

Step 3: Configure TypeScript

Create a tsconfig.json file in the root of your project with the following configuration:

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    }
}

Step 4: Create project structure

Create a src directory in the root of your project. Inside the src directory, create the following files:

  • main.ts: This will be the entry point for the main process.
  • renderer.ts: This will be the entry point for the renderer process.
  • index.html: This is the HTML file that will be loaded in the browser window.

Here is a basic index.html file:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF - 8">
    <meta name="viewport" content="width=device - width, initial - scale=1.0">
    <title>Electron TypeScript App</title>
</head>

<body>
    <h1>Hello, Electron with TypeScript!</h1>
    <script src="renderer.js"></script>
</body>

</html>

Usage Methods

Main Process (main.ts)

import { app, BrowserWindow } from 'electron';

function createWindow() {
    const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    win.loadFile('src/index.html');
}

app.whenReady().then(() => {
    createWindow();

    app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
});

app.on('window - all - closed', function () {
    if (process.platform!== 'darwin') app.quit();
});

Renderer Process (renderer.ts)

// You can use web APIs here
const heading = document.querySelector('h1');
if (heading) {
    heading.textContent = 'Updated heading from TypeScript!';
}

Transpile and Run

Transpile the TypeScript code using the TypeScript compiler:

npx tsc

Run the Electron application:

npx electron.

Common Practices

Error Handling

  • Main Process: In the main process, use try - catch blocks when dealing with asynchronous operations like file reading or network requests.
async function someAsyncOperation() {
    try {
        // Perform some asynchronous operation
    } catch (error) {
        console.error('Error in main process:', error);
    }
}
  • Renderer Process: Use try - catch blocks around code that might throw errors, especially when interacting with the DOM.
try {
    const element = document.getElementById('non - existent - element');
    element.textContent = 'This will throw an error if the element doesn\'t exist';
} catch (error) {
    console.error('Error in renderer process:', error);
}

IPC Communication

When communicating between the main process and the renderer process, use the ipcMain and ipcRenderer modules.

Main Process (main.ts)

import { app, BrowserWindow, ipcMain } from 'electron';

ipcMain.on('message - from - renderer', (event, arg) => {
    console.log('Received message from renderer:', arg);
    event.sender.send('message - from - main', 'Message received in main process');
});

Renderer Process (renderer.ts)

import { ipcRenderer } from 'electron';

ipcRenderer.send('message - from - renderer', 'Hello from renderer');
ipcRenderer.on('message - from - main', (event, arg) => {
    console.log('Received message from main:', arg);
});

Best Practices

Code Organization

  • Separate Concerns: Keep the main process and renderer process code separate. Use modular design principles to break down your code into smaller, more manageable functions and classes.
  • Use Interfaces: Define interfaces for data structures passed between the main and renderer processes. This makes the code more self - documenting and easier to maintain.

Security

  • Context Isolation: Enable context isolation in the webPreferences of the BrowserWindow to prevent the renderer process from directly accessing Node.js APIs. This helps protect against potential security vulnerabilities.
const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
        nodeIntegration: false,
        contextIsolation: true,
        preload: path.join(__dirname, 'preload.js')
    }
});

Performance

  • Lazy Loading: If your application has large components or data, consider using lazy loading techniques to improve startup performance.

Conclusion

Combining Electron with TypeScript provides a powerful and robust way to build cross - platform desktop applications. By following the fundamental concepts, usage methods, common practices, and best practices outlined in this blog post, you can create high - quality Electron applications with TypeScript. Remember to keep your code organized, handle errors properly, and prioritize security and performance.

References