Introduction to TypeScript
What is TypeScript
TypeScript is an open-source language maintained and developed by Microsoft.
Basically, TypeScript adds additional syntax to JavaScript to support a tighter integration with your editor. Catch errors early in your editor or in your CI/CD pipeline, and write more maintainable code.
We can talk about other TypeScript benefits later, let's see some examples now!
First TypeScript code
Take a look at this code snippet and then we can unpack it together:
type type User = {
name: string;
age: number;
}
User = {
name: string
name: string;
age: number
age: number;
};
function function isAdult(user: User): boolean
isAdult(user: User
user: type User = {
name: string;
age: number;
}
User): boolean {
return user: User
user.age: number
age >= 18;
}
const const justine: {
name: string;
age: number;
}
justine = {
name: string
name: 'Justine',
age: number
age: 23,
} satisfies type User = {
name: string;
age: number;
}
User;
const const isJustineAnAdult: boolean
isJustineAnAdult = function isAdult(user: User): boolean
isAdult(const justine: {
name: string;
age: number;
}
justine);
The first part (with the type
keyword) is responsible for declaring our custom object type representing users. Later we utilize this newly created type to create the function isAdult
that accepts one argument of type User
and returns a boolean
. After this, we create justine
, our example data that can be used for calling the previously defined function. Finally, we create a new variable with information on whether justine
is an adult.
There are additional things about this example that you should know. Firstly, if we do not comply with the declared types, TypeScript will inform us that something is wrong and prevent misuse. Secondly, not everything must be typed explicitly—TypeScript infers types for us. For example, the variable isJustineAnAdult
is of type boolean
even if we didn't type it explicitly, and justine
would be a valid argument for our function even though we didn't declare this variable as of User
type.
What does TypeScript consist of?
TypeScript consists of two main components: the code itself and type definitions.
TypeScript Code
The code part is regular JavaScript with additional TypeScript-specific syntax for type annotations. When TypeScript code is compiled, all the TypeScript-specific parts are removed, resulting in clean JavaScript that can run in any environment. For example:
function function greet(name: string): void
greet(name: string
name: string) {
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(`Hello, ${name: string
name}!`);
}
Type Definitions
Type definitions describe the shape of existing JavaScript code. They are usually stored in .d.ts
files and don't contain any actual implementation—they only describe the types. These definitions are essential for interoperability with JavaScript: code is not usually distributed as TypeScript, but instead transpiled to JavaScript that includes sidecar type definition files.
For example, when you use Node.js with TypeScript, you'll need type definitions for Node.js APIs. This is available via @types/node
. Install it using:
npm add --save-dev @types/node
These type definitions allow TypeScript to understand Node.js APIs and provide proper type checking and autocompletion when you use functions like fs.readFile
or http.createServer
. For example:
import * as module "fs"
The `node:fs` module enables interacting with the file system in a
way modeled on standard POSIX functions.
To use the promise-based APIs:
```js
import * as fs from 'node:fs/promises';
```
To use the callback and sync APIs:
```js
import * as fs from 'node:fs';
```
All file system operations have synchronous, callback, and promise-based
forms, and are accessible using both CommonJS syntax and ES6 Modules (ESM).fs from 'fs';
module "fs"
The `node:fs` module enables interacting with the file system in a
way modeled on standard POSIX functions.
To use the promise-based APIs:
```js
import * as fs from 'node:fs/promises';
```
To use the callback and sync APIs:
```js
import * as fs from 'node:fs';
```
All file system operations have synchronous, callback, and promise-based
forms, and are accessible using both CommonJS syntax and ES6 Modules (ESM).fs.function readFile(path: fs.PathOrFileDescriptor, options: ({
encoding?: null | undefined;
flag?: string | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | undefined | null, callback: (err: NodeJS.ErrnoException | null, data: NonSharedBuffer) => void): void (+3 overloads)
Asynchronously reads the entire contents of a file.
```js
import { readFile } from 'node:fs';
readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
```
The callback is passed two arguments `(err, data)`, where `data` is the
contents of the file.
If no encoding is specified, then the raw buffer is returned.
If `options` is a string, then it specifies the encoding:
```js
import { readFile } from 'node:fs';
readFile('/etc/passwd', 'utf8', callback);
```
When the path is a directory, the behavior of `fs.readFile()` and
{@link
readFileSync
}
is platform-specific. On macOS, Linux, and Windows, an
error will be returned. On FreeBSD, a representation of the directory's contents
will be returned.
```js
import { readFile } from 'node:fs';
// macOS, Linux, and Windows
readFile('<directory>', (err, data) => {
// => [Error: EISDIR: illegal operation on a directory, read <directory>]
});
// FreeBSD
readFile('<directory>', (err, data) => {
// => null, <data>
});
```
It is possible to abort an ongoing request using an `AbortSignal`. If a
request is aborted the callback is called with an `AbortError`:
```js
import { readFile } from 'node:fs';
const controller = new AbortController();
const signal = controller.signal;
readFile(fileInfo[0].name, { signal }, (err, buf) => {
// ...
});
// When you want to abort the request
controller.abort();
```
The `fs.readFile()` function buffers the entire file. To minimize memory costs,
when possible prefer streaming via `fs.createReadStream()`.
Aborting an ongoing request does not abort individual operating
system requests but rather the internal buffering `fs.readFile` performs.readFile('example.txt', foo, (err: NodeJS.ErrnoException | null
err, data: NonSharedBuffer
data) => {
// Argument of type '"foo"' is not assignable to parameter of type …
if (err: NodeJS.ErrnoException | null
err) {
throw err: NodeJS.ErrnoException
err;
}
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(data: NonSharedBuffer
data);
});
Many popular JavaScript libraries have their type definitions available under the @types
namespace, maintained by the DefinitelyTyped community. This enables seamless integration of existing JavaScript libraries with TypeScript projects.
Transform Capabilities
TypeScript also includes powerful transformation capabilities, particularly for JSX (used in React and similar frameworks). The TypeScript compiler can transform JSX syntax into regular JavaScript, similar to how Babel works. While we won't cover these transformation features in these articles, it's worth noting that TypeScript isn't only a tool for type checking—it's also a build tool for transforming modern JavaScript syntax into compatible versions for different environments.
How to run TypeScript code
Okay, so we have some TypeScript code. Now how do we run it? There are few possible ways to run TypeScript code, we will cover all of them in the next articles.