Cannot Find Name "require"

|5 min read|
Adam
Adam


Explore Other Articles from This Series

To prod

I wanted to use NodeJS require directive to read a JSON file inside my TypeScript application, but...

Prerequisites

  • NodeJS Long Time Support (Version 12.x)
  • NodeJS Package Manager (Version 6.x)
  • TypeScript (Version 3.9.x)
  • The configuration.json file with the valid JSON content:

    {
      "api_url": "https://api.localhost",
      "api_key": "ZXH123"
    }

TL;DR

Life without TypeScript

NodeJS, which uses CommonJS module format, offers a simple way to read JSON files via require directive; it is a synchronous operation and the file contents will be cached under require.cache object. To demonstrate it, let's create a simple ConfigReader.js script which is going to load the configuration.json file as shown below:

const configuration = require('./configuration');

console.log(configuration.api_url, configuration.api_key);

After executing this script with node ConfigReader.js, we shall see the following output https://api.localhost ZXH123. Nothing extraordinary here, yet the following things are worth noting:

  • require does not care about the file extension, as long as it is either .js or .json. However, if you created the configuration.js file with the following content module.exports = { api_url: 'url', api_key: 'key' }, it would be loaded before the configuration.json file.
  • if the file was not a valid JSON file, the above script would fail by raising an exception. You could catch it with try-catch block if needed.
  • if you wanted to read a file in a non-blocking mode or it was not encoded in Unicode/UTF-8, then fs core module should be used instead.

Until now, we have been tinkering with NodeJS and pure JavaScript. Let's switch gears and reproduce the same functionality in TypeScript.

Embracing TypeScript

Let's create a simple file TypescriptConfigReader.ts with exactly the same content as before. We have to suffix it with .ts because TypeScript Compiler supports only the following extensions: .ts, .tsx, .d.ts.

Confusingly enough, we could execute this file with node TypescriptConfigReader.ts and get the same output, without any errors. There are two reasons for it:

  • First, the NodeJS Interpreter does not bother about the file extension so it can run any file that has a valid JavaScript code and uses CommonJS module format.
  • Second, TypeScript is a superset of JavaScript and provides optional static typing, thus every valid JavaScript file is also understood by the TypeScript Compiler. By default, TypeScript is pretty permissive, but you can tighten this behavior up: for more information, please refer to TypeScript Guide: Migration from JavaScript

As soon as we compile the TypescriptConfigReader.ts file with: tsc TypescriptConfigReader.ts, we will instantly see the following compilation error:

TypescriptConfigReader.ts:1:23 - error TS2580: Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i @types/node`.

Strangely enough, the TypescriptConfigReader.js file has also been created and it can be run without any issues via NodeJS Interpreter. So what is going on here?

  • TypeScript Compiler is special; instead of compiling your TypeScript code straight into bytecode, it compiles it to JavaScript code and nothing more. How this JavaScript code gets converted into the bytecode is the responsibility of JavaScript Runtime that lives in your browser, NodeJS, or whatever other JavaScript engines you may be using.
  • The types you declare in your TypeScript code are used only for typechecking and they will never affect your program's generated output. This is a good thing because you can slowly introduce TypeScript into your JavaScript project without the risk of breaking it.
  • TypeScript Compiler is pretty configurable, so you might run it like tsc --noEmitOnError TypescriptConfigReader.ts. In this way, no JavaScript files would be emitted if any compilation errors were reported.

As shown above, even one line of code can make the TypeScript Compiler unhappy. Let's move on and explore the solution space together.

Solution Area

By shouting with the TS2580 error code, the TypeScript Compiler gave us also a decent hint on how to fix it. Let's follow its advice to some extent and install the proper Type Declarations with the following command:

npm i @types/node --save-dev

If you run the tsc TypescriptConfigReader.ts now, TypeScript Compiler should not complain anymore. That was simple, but before wrapping it up, let's clarify a few things:

  • If you don't have package.json file, npm command will not create one for you. Generally speaking, it is better to save every dependency in package.json file, so your coworkers have the same state of your applications as you do.
  • If you do have the package.json file, npm command will save @types/node under devDependencies. In the end, TypeScript is a development tool and TypeScript types do not exist at runtime.
  • You can easily find any needed @types/* packages on TypeSearch or nmpjs.com
  • You can read more about Type Declarations from TypeScript Handbook and check a community-maintained collection of Type Definitions for JavaScript libraries: DefinitelyTyped.
  • If you intend to use globals (i.e.: __dirname, __filename) or any other NodeJS built-in core modules (i.e.: fs, http), you are covered. All of them are defined under the same @types/node Type Declarations.