This blog post gives the bird’s eye view of how TypeScript works: What is the structure of a typical TypeScript project? What is compiled and how? How can we use IDEs to write TypeScript?
This post is meant to be read before learning how to write TypeScript code (material for doing that is listed at the end).
This is one possible file structure for TypeScript projects:
typescript-project/
dist/
ts/
src/
main.ts
util.ts
test/
util_test.ts
tsconfig.json
Explanations:
ts/
contains the TypeScript files:
ts/src/
contains the actual code.ts/test/
contains tests for the code.dist/
is where the output of the compiler is stored.ts/src/main.ts
to a JavaScript file dist/src/main.js
(and possibly other files).tsconfig.json
is used to configure the TypeScript compiler.tsconfig.json
The contents of tsconfig.json
look as follows:
{
"compilerOptions": {
"rootDir": "ts",
"outDir": "dist",
"module": "commonjs",
···
}
}
We have specified that:
ts/
.dist/
.Visual Studio Code is one of the most popular IDEs for writing TypeScript code. In order to use it well, we need to understand that TypeScript source code is processed in two independent ways:
Checking open editors for errors: This is done via a so-called language server. They are an editor-independent way of providing editors with language-related services (detecting errors, refactorings, auto-completions, etc.). Editors (such as IDEs) communicate with language servers via a special protocol (JSON-RPC, i.e. JSON-based remote procedure calls). That enables one to write such servers in almost any programming language.
Building (compiling TypeScript files to JavaScript files): Here, we have two choices.
tsc
has a --watch
mode that watches input files and compiles them to output files whenever they change. As a consequence, whenever we save a TypeScript file in the IDE, we immediately get the corresponding output file(s).tsc
from within Visual Studio Code. In order to do so, it must be installed either inside project that we are currently working on or globally (via the Node.js package manager npm).With building, we get a complete list of errors. For more information on compiling TypeScript in Visual Studio Code, see the official documentation for that IDE.
Given a TypeScript file main.ts
, the TypeScript compiler can produce several kinds of artifacts. The most common ones are:
main.js
main.d.ts
(contains type information; think .ts
file minus the JavaScript code)main.js.map
TypeScript is often not delivered via .ts
files, but via .js
files and .d.ts
files:
A source map specifies for each part of the output code in main.js
, which part of the input code in main.ts
produced it. Among other things, this information enables runtime environments to execute JavaScript code, while showing the line numbers of the TypeScript code in error messages.
The npm registry is a huge repository of JavaScript code. If we want to use a JavaScript package from TypeScript, we need type information for it:
.d.ts
files or even the complete TypeScript code.The declaration files of DefinitelyTyped reside in the @types
namespace. Therefore, if we need a declaration file for a package such as lodash
, we have to install the package @types/lodash
.
The TypeScript compiler can also process plain JavaScript files:
With the option --allowJs
, the TypeScript compiler copies JavaScript files in the input directory over to the output directory. Benefit: When migrating from JavaScript to TypeScript we can start with a mix of JavaScript and TypeScript files and slowly convert more JavaScript files to TypeScript.
With the option --checkJs
, the compiler additionally type-checks JavaScript files (--allowJs
must be on for this option to work). It does so as well as it can, given the limited information that is available.
// @ts-nocheck
, it will not be type-checked.--checkJs
, the comment // @ts-check
can be used to type-check individual JavaScript files.The TypeScript compiler uses static type information that is specified via JSDoc comments (see below for an example). If we are thorough, we can fully statically type plain JavaScript files and even derive declaration files from them.
With the option --noEmit
, the compiler does not produce any output, it only type-checks files.
This is an example of a JSDoc comment that provides static type information for a function add()
:
/**
* @param {number} x - A number param.
* @param {number} y - A number param.
* @returns {number} This is the result
*/
function add(x, y) {
return x + y;
}
More information: Type-Checking JavaScript Files in the TypeScript Handbook.