Node Guide

Menu
Menu

Typescript

Why Typescript?

JavaScript is considered a loosely typed language because it does not require explicit declaration of variable types. In JavaScript, you can assign a value of any type to a variable without specifying its type beforehand. This flexibility allows for easy and dynamic programming but can also lead to potential bugs and errors.

For example, in JavaScript, you can write code like this:

As you can see, the variable x is assigned different types of values without any restrictions. This can make it challenging to catch errors at compile-time or detect type-related issues early in the development process.

TypeScript, on the other hand, is a superset of JavaScript that introduces static typing to the language. It adds optional type annotations, allowing developers to specify the types of variables, function parameters, and return values. TypeScript code goes through a compilation process that checks for type errors and enforces type safety.

Typescript is developed and maintained by Microsoft so has great integration with tools like Visual Studio Code.

By using TypeScript, developers can catch type-related bugs and errors during development rather than discovering them at runtime. It provides benefits such as:

  1. Early detection of errors: TypeScript's compiler can catch type errors during the development process. It helps identify issues like assigning a value of the wrong type to a variable or passing incorrect arguments to a function.

  2. Enhanced code documentation: Type annotations serve as documentation, making it easier for developers to understand how different parts of the codebase interact and what types of values they expect.

  3. Improved code maintainability: TypeScript makes code easier to understand and maintain by providing clear interfaces and type definitions. It helps other developers or future maintainers of the codebase by providing more explicit information about the expected types.

  4. IDE support and tooling: TypeScript is supported by many popular code editors and integrated development environments (IDEs), providing features like autocompletion, code navigation, and refactoring tools that leverage the type information.

  5. Gradual adoption: TypeScript allows for gradual adoption, meaning you can start using it in existing JavaScript projects without needing to rewrite the entire codebase. You can gradually add type annotations to specific files or modules, and TypeScript will still compile and work with the JavaScript parts of the code.

These advantages have made TypeScript a popular choice for JavaScript developers, especially in larger projects or teams where maintaining code quality and minimizing errors are crucial. It combines the flexibility of JavaScript with the benefits of static typing, offering a robust and reliable development experience.

  1. Static Typing: TypeScript enforces static types, catching errors at compile-time and improving code quality, resulting in fewer bugs and enhanced reliability.

  2. Code Maintainability and Readability: TypeScript's static typing makes code self-documenting, improving understandability, and reducing errors during modifications or collaborations.

  3. ES6+ Features and Compatibility: TypeScript supports modern JavaScript features and can compile down to older versions, allowing for cleaner code while ensuring compatibility across different Node.js versions.

  4. Strong Module System: TypeScript provides a powerful module system that aligns well with Node.js, enabling better code organization and integration with external libraries and tools.

    You can use the import and export keywords to organize and structure your code into reusable modules, improving code organization and separation of concerns.

These benefits of TypeScript contribute to a more robust, maintainable, and readable codebase for your Node/Express API application.

Setting up a Typescript Project

To set up TypeScript in Visual Studio Code, follow these steps:

  1. Create a new project: Open a new folder where you want to create your TypeScript project or navigate to an existing project folder.

  2. Initialize the project: Open the integrated terminal in Visual Studio Code by selecting View -> Terminal or by pressing Ctrl + .

  3. Initialize npm: In the terminal, run the following command to initialize a new npm project with the default settings:

    npm init -y

    This will create a package.json file for the node project.

  4. Install TypeScript: In the terminal, run the following command to install TypeScript as a dev dependency:

    npm install --save-dev typescript

    This will download and install the TypeScript compiler.

  5. Create a TypeScript configuration file: In the terminal, run the following command to generate a tsconfig.json file:

    npx tsc --init

    npx is a tool that comes bundled with npm that is designed to execute packages without having to install them globally or locally.

    This command generates a basic tsconfig.json file that we will customize to configure TypeScript for our project.

Running a Basic Typescript File

Before refining the tsconfig.json file let's have a little experiment with Typescript.

You can also use the Typescript Playground to experiment.

Typescript files are created with a .ts extension which Visual Studio Code will recognize and assign the icon to. In the root of your application create a simple file called example.ts with the following:

example.ts

In this example, we have a function called greet that takes a parameter name of type string and returns a string. We then call this function with the argument "Martin", store the result in a variable result, and log it to the console.

To compile this TypeScript file in the terminal run the following command:

tsc example.ts

This command instructs the TypeScript compiler (tsc) to compile the example.ts file. The compiler will generate an equivalent JavaScript file called example.js in the same directory.

tsc

You can then execute the resulting JavaScript file using a JavaScript runtime environment like Node.js:

node example.js

Running this command will output "Hello, Martin!" to the console, demonstrating the successful compilation and execution of the TypeScript code.

The above illustrates how Typescript helps in Javascript development, but our application will get messy if we have a folder full of matching .ts and .js files. It would make more sense to place all the .ts files in one folder, and all the .js in another. To do this we need to configure Typescript via the tsconfig.json file.

Configuring Typescript via the tsconfig.json File

The tsconfig.json as the name implies is the configuration file for working with Typescipt and should be placed at the route of your application.

Open the tsconfig.json created above. It comes with a range of comments and a link to the further details about the file. Remove the default code to replace it with:

tsconfig.json

Let's break down the settings applied in this tsconfig.json file:

  1. "compilerOptions": This section specifies the compiler options for TypeScript.

    • "module": Specifies the module system to use. In this case, "commonjs" is used, which is the module system commonly used in Node.js.

    • "esModuleInterop": Enables compatibility with modules that use default exports. Setting it to true allows for more seamless interoperability between CommonJS and ES modules.

    • "target": Specifies the ECMAScript version to target. In this case, "es6" is targeted, which allows using modern JavaScript features.

    • "moduleResolution": Specifies how modules are resolved. "node" is used, which aligns with Node.js module resolution.

    • "outDir": Specifies the output directory for compiled JavaScript files. In this case, "dist" is set as the output directory. You do not need to create this directory typescript will create it on compilation.

    • "baseUrl": Specifies the base path for module resolution. In this case, "." indicates the current directory.

    • "paths": Configures module name mappings for module resolution. The "*" key represents all module names. The value ["node_modules/*", "src/types/*"] specifies the paths where TypeScript should look for module definitions. It searches in the node_modules directory and also in the src/types directory.

  2. "include": Specifies which files should be included for compilation. In this case, "src/**/*.ts" includes all TypeScript files within the src directory and its subdirectories. We need to create the src folder for this purpose and place our .ts files in this folder.

  3. "exclude": Specifies which files or directories to exclude from compilation. In this case, "node_modules" is excluded to prevent TypeScript from attempting to compile files in the node_modules directory.

These settings ensure that TypeScript is configured to compile the TypeScript files located in the src directory and its subdirectories. The compiled JavaScript files are output to the dist directory. The module resolution is set to align with Node.js, and the paths are configured to search for modules in the specified directories.

TypeScript Typings

Before applying Typescript to a Node project we must look at typescript types. Node applications make use of dependencies located in the node_modules folder and we need to ensure that Typescript can deal with this code as well as that we produce. As such when writing TypeScript code for a Node.js project, it's important to have the appropriate typings to provide type information for the Node.js-specific APIs and modules. The @types/node package contains the TypeScript declaration files that define the types for the Node.js runtime and its core modules.

By installing @types/node as a development dependency (--save-dev flag), you ensure that the typings are available during the development process and are not required in the production environment. These typings allow the TypeScript compiler to understand and provide accurate type information for the Node.js-specific APIs, allowing for better type checking, autocompletion, and error detection.

Some examples of the Node.js-specific APIs and modules covered by @types/node include:

  1. fs: The File System module in Node.js, which allows you to work with files and directories.

  2. http and https: These modules are used for creating HTTP and HTTPS servers and making HTTP requests.

  3. os: The Operating System module, which provides information about the operating system on which Node.js is running.

  4. path: The Path module, which provides utilities for working with file and directory paths.

  5. By installing and using @types/node, you ensure that your TypeScript code can interact with the Node.js runtime and its APIs in a type-safe manner, helping to catch errors and improve the development experience.

    To install Typescript typings run:

    npm install --save-dev @types/node

    Setting up the Basic Node/Express entry file

    Create a new file src/index.ts and add the following basic setup for a node express application:

    src/index.ts

    Notice that Visual Studio Code will pick up the require as an error and suggest converting it to an import.

    Change the express require line to:

    SNIPPET: src/index.ts
    Ensure you have installed the express npm package. Do so with
    npm install express

    You should now be able to run tsc to compile this code and you will see the file created in dist/index.js.

    This can be done via the dist folder but it will be easier to update the package.json file for the node app:

    Update package.json with:

    SNIPPET: package.json

    We can also use nodemon to make saving and testing easier. Install nodemon with:

    npm install --save-dev typescript ts-node nodemon

    With these changes, you can run npm run build to compile your TypeScript code into JavaScript and generate the output files in the dist folder. Then, you can use npm start to run the compiled JavaScript code.

    Note: If you want to automatically restart the server during development, you can use nodemon by modifying the start command as follows:

    SNIPPET: package.json

    This will enable automatic restarts whenever changes are detected in your TypeScript files.

    Importing Typescript Modules

    In Node.js, TypeScript typings are used to provide type information for JavaScript libraries and modules, enabling TypeScript to perform static type checking and provide better developer tooling support.

    When working with Node.js modules in TypeScript, it's important to have the appropriate TypeScript typings installed for the modules you're using. These typings typically come in the form of TypeScript declaration files (.d.ts files) that provide type information for the corresponding JavaScript modules.

    TypeScript typings allow the TypeScript compiler to understand the structure, types, and properties of external modules, providing better code completion, error checking, and type inference. Without typings, the TypeScript compiler would treat the imported modules as of type any, resulting in limited type safety and missing out on the benefits TypeScript provides.

    To use TypeScript typings for a module, you typically need to follow these steps:

    1. Install the corresponding TypeScript typings package: Many popular modules have dedicated TypeScript typings packages available on npm. These packages have names starting with @types/ followed by the module name. For example, the typings for the express module can be installed using npm install @types/express.

    2. Import the module using the appropriate import statement: In TypeScript, you import modules using the import statement. The TypeScript typings allow you to import modules and reference their types correctly. For example, import express from 'express' imports the express module and assigns its type information to the express variable.

    3. Use the imported module with type information: Once the module is imported, you can use it with the correct type information provided by the typings. This enables TypeScript to perform type checking and provide autocompletion for the module's properties and methods.

    By installing and using TypeScript typings for Node.js modules, you can leverage TypeScript's static type checking capabilities to catch errors and provide better development experiences while working with external libraries and modules.

    For example if we wish to use the dotenv module for environmental variable we need to install the @types/dotenv module as follows:

    npm install dotenv @types/dotenv

    ... and also for node:

    npm install --save-dev @types/node

    Next: Building an API App with Typescript