This article describes how to create a Node.js application written in TypeScript. Full code can be found in this GitHub repository.
Let's first initialize our Node project:
TypeScript has a command-line compiler which transforms TypeScript code into JavaScript. It comes in a form of a Node package named typescript. Let's install it as a development dependency:
package.json now contains:
Because we installed TypeScript for the current project only (not globally), TypeScript compiler, tsc, is not available from the command line. We can run it in three ways:
From DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.
This is the preferred method
npm install --save-dev @types/node
The types should then be automatically included by the compiler.
For an NPM package "foo", typings for it will be at "@types/foo".
typescript - How do I decide whether @types/* goes into `dependencies` or `devDependencies`? - Stack Overflow
Let's see what are the command line arguments and options of tsc:
Let's add to the project some TypeScript file.
..\demo-typescript\src\index.ts:
Let's follow an example from tsc --help in order to get TS code compiled into JavaScript:
After this command is executed build/index.js file is created and it looks like this:
We can run it with Node.js:
It is not possible to specify compiling files from entire directory via command line arguments - all files have to be explicitly listed. This and also listing all arguments in the command line is not practical. Solution for this is specifying all options in the configuration file and instructing tsc to read everything from it.
TypeScript transpiler uses a config file tsconfig.json. To create , use:
---
If we installed TypeScript as a local package (and possibly as dev dependency) via:
$ npm install typescript --save-dev
...and if we added to package.json:
"scripts": {
...
"tsc": "tsc"
},
...then we can call tsc init via:
$ npm run tsc -- --init
---
Default content of tsconfig.json is:
If we execute
...tsc will compile all .ts files in the project and place matching .js files next to .ts files.
To specify output directory (named e.g. "bundle", "dist" or "build") into which we want output structure to be placed in, we can set in config file:
npx tsc will now output all .js files (preserving their original directory tree) in the bundle directory.
Let's now add build and start scripts to package.json so we don't have to use npx and run tools manually but follow the common approach - use npm as the task runner:
..\demo-typescript\package.json:
Let's build the app:
I used name build-ts for script which runs tsc but this script is usually named just tsc.
In case you get an error like this:
$ npm run tsc
...
error TS18003: No inputs were found in config file '/path/to/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '[]'.
Makes sure you have .ts (not .js!) files in the directory (or its subdirectories) where tsconfig.json resides.
In case you get an error like this:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
node_modules/@types/babel__template/index.d.ts:16:28 - error TS2583: Cannot find name 'Set'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.
16 placeholderWhitelist?: Set<string>;
~~~
Found 1 error.
...simply change tsconfig.json to contain:
"lib": ["es2015"],
If you get error like this:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
src/main.ts:1:1 - error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.
1 console.log("Hello, world")
~~~~~~~
Found 1 error.
...follow the suggestion and add 'dom' to the lib property:
"lib": ["es2015", "dom"],
Error example:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
tsconfig.json:5:5 - error TS6082: Only 'amd' and 'system' modules are supported alongside --outFile.
5 "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
~~~~~~~~
tsconfig.json:13:5 - error TS6082: Only 'amd' and 'system' modules are supported alongside --outFile.
13 "outFile": "./build/wpsync.js", /* Concatenate and emit output to single file. */
~~~~~~~~~
Found 2 errors.
In "module": "commonjs", replace commonjs with system:
xxx
module property in tsconfig.json can be set to one of the following values: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
If we want to make tsc bundling build output into a single file we have to use outFile property but this in turn requires module to be set to either amd or system.
If modules is set to amd (Asynchronous Module Definition [Wikipedia]) then our project needs to use library which implements AMD e.g. RequireJS.
This is the example of the tsc output in case when module is set to amd:
"use strict";
define("src/greeter", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function greeter(name) {
return "Hello, " + name;
}
exports.greeter = greeter;
});
define("src/main", ["require", "exports", "src/greeter"], function (require, exports, greeter_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log(greeter_1.greeter('Bojan'));
});
If we try to run it without setting RequireJS beforehand:
$ node app.js
...we'll get an error:
ReferenceError: define is not defined
Uncaught ReferenceError: define is not defined typescript
How to run a TypeScript app compiled into a single file with AMD modules?
RequireJS is module loader used mostly in web application and is not native for Node.js applications.
If modules is set to system then our project needs to use SystemJS.
This is the example of the tsc output in case when module is set to system:
"use strict";
System.register("src/greeter", [], function (exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
function greeter(name) {
return "Hello, " + name;
}
exports_1("greeter", greeter);
return {
setters: [],
execute: function () {
}
};
});
System.register("src/main", ["src/greeter"], function (exports_2, context_2) {
"use strict";
var greeter_1;
var __moduleName = context_2 && context_2.id;
return {
setters: [
function (greeter_1_1) {
greeter_1 = greeter_1_1;
}
],
execute: function () {
console.log(greeter_1.greeter('Bojan'));
}
};
});
If we try to run it without setting SystemJS beforehand:
$ node app.js
...we'll get an error:
ReferenceError: System is not defined
How do I fix “ReferenceError: System is not defined” when running script from command line?
If we don't want to use RequireJS or SystemJS then we can't use tsc to compile our TS code into a single file bundle. We need to use some bundler...like Rollup.js. Seems that the answer How do I compile my TypeScript code for Node.js to one file? is still actual.
Default value for module property is commonjs. This means that - CommonJS is gonna be used and CommonJS is a "native" module loader for Node.js applications.
Promise
(
executor:
(
resolve: (value?: void | PromiseLike<void> | undefined) => void,
reject: (reason?: any) => void
) => void
): Promise<void>
TypeScript Deep Dive: Promise
The return type of an async function or method must be the global Promise<T> type:
.ts file becomes a module if it exports types, enums, functions, classes or interfaces.
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. [https://www.typescriptlang.org/docs/handbook/modules.html]
Export syntax:
Import syntax:
can be replaced with shorter form:
Let's first initialize our Node project:
>npm init -y
Installation
TypeScript has a command-line compiler which transforms TypeScript code into JavaScript. It comes in a form of a Node package named typescript. Let's install it as a development dependency:
>npm install typescript --save-dev
npm notice created a lockfile as package-lock.json. You should commit this file.
+ typescript@3.1.1
added 1 package from 1 contributor and audited 1 package in 1.318s
found 0 vulnerabilities
npm notice created a lockfile as package-lock.json. You should commit this file.
+ typescript@3.1.1
added 1 package from 1 contributor and audited 1 package in 1.318s
found 0 vulnerabilities
package.json now contains:
{
...
"devDependencies": {
"typescript": "^3.1.1"
}
}
...
"devDependencies": {
"typescript": "^3.1.1"
}
}
Because we installed TypeScript for the current project only (not globally), TypeScript compiler, tsc, is not available from the command line. We can run it in three ways:
- directly as ./node_modules/.bin/tsc
- via npx tool,
- via npm run tsc if we add it as a script in package.json as:
...
"scripts": {
"tsc":"tsc"
...
}
...
"scripts": {
"tsc":"tsc"
...
}
...
How to install declaration files for npm packages I am using?
From DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.
This is the preferred method
npm install --save-dev @types/node
The types should then be automatically included by the compiler.
For an NPM package "foo", typings for it will be at "@types/foo".
typescript - How do I decide whether @types/* goes into `dependencies` or `devDependencies`? - Stack Overflow
TypeScript Compiler
Let's see what are the command line arguments and options of tsc:
>npx tsc
Version 3.1.1
Syntax: tsc [options] [file...]
Examples: tsc hello.ts
tsc --outFile file.js file.ts
tsc @args.txt
tsc --build tsconfig.json
Options:
-h, --help Print this message.
-w, --watch Watch input files.
--all Show all compiler options.
-v, --version Print the compiler's version.
--init Initializes a TypeScript project and creates a tsconfig.json file.
-p FILE OR DIRECTORY, --project FILE OR DIRECTORY Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'.
-b, --build Build one or more projects and their dependencies, if out of date
--pretty Stylize errors and messages using color and context (experimental).
-t VERSION, --target VERSION Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'.
-m KIND, --module KIND Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
--lib Specify library files to be included in the compilation.
'es5' 'es6' 'es2015' 'es7' 'es2016' 'es2017' 'es2018' 'esnext' 'dom' 'dom.iterable' 'webworker' 'webworker.importscripts' 'scripthost' 'es2015.core' 'es2015.collection' 'es2015.generator' 'es2015.iterable' 'es2015.promise' 'es2015.proxy' 'es2015.reflect' 'es2015.symbol' 'es2015.symbol.wellknown' 'es2016.array.include' 'es2017.object' 'es2017.sharedmemory' 'es2017.string' 'es2017.intl' 'es2017.typedarrays' 'es2018.intl' 'es2018.promise' 'es2018.regexp' 'esnext.array' 'esnext.symbol' 'esnext.asynciterable' 'esnext.intl'
--allowJs Allow javascript files to be compiled.
--jsx KIND Specify JSX code generation: 'preserve', 'react-native', or 'react'.
-d, --declaration Generates corresponding '.d.ts' file.
--declarationMap Generates a sourcemap for each corresponding '.d.ts' file.
--sourceMap Generates corresponding '.map' file.
--outFile FILE Concatenate and emit output to single file.
--outDir DIRECTORY Redirect output structure to the directory.
--removeComments Do not emit comments to output.
--noEmit Do not emit outputs.
--strict Enable all strict type-checking options.
--noImplicitAny Raise error on expressions and declarations with an implied 'any' type.
--strictNullChecks Enable strict null checks.
--strictFunctionTypes Enable strict checking of function types.
--strictPropertyInitialization Enable strict checking of property initialization in classes.
--noImplicitThis Raise error on 'this' expressions with an implied 'any' type.
--alwaysStrict Parse in strict mode and emit "use strict" for each source file.
--noUnusedLocals Report errors on unused locals.
--noUnusedParameters Report errors on unused parameters.
--noImplicitReturns Report error when not all code paths in function return a value.
--noFallthroughCasesInSwitch Report errors for fallthrough cases in switch statement.
--types Type declaration files to be included in compilation.
--esModuleInterop Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.
@<file> Insert command line options and files from a file.
Version 3.1.1
Syntax: tsc [options] [file...]
Examples: tsc hello.ts
tsc --outFile file.js file.ts
tsc @args.txt
tsc --build tsconfig.json
Options:
-h, --help Print this message.
-w, --watch Watch input files.
--all Show all compiler options.
-v, --version Print the compiler's version.
--init Initializes a TypeScript project and creates a tsconfig.json file.
-p FILE OR DIRECTORY, --project FILE OR DIRECTORY Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'.
-b, --build Build one or more projects and their dependencies, if out of date
--pretty Stylize errors and messages using color and context (experimental).
-t VERSION, --target VERSION Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'.
-m KIND, --module KIND Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
--lib Specify library files to be included in the compilation.
'es5' 'es6' 'es2015' 'es7' 'es2016' 'es2017' 'es2018' 'esnext' 'dom' 'dom.iterable' 'webworker' 'webworker.importscripts' 'scripthost' 'es2015.core' 'es2015.collection' 'es2015.generator' 'es2015.iterable' 'es2015.promise' 'es2015.proxy' 'es2015.reflect' 'es2015.symbol' 'es2015.symbol.wellknown' 'es2016.array.include' 'es2017.object' 'es2017.sharedmemory' 'es2017.string' 'es2017.intl' 'es2017.typedarrays' 'es2018.intl' 'es2018.promise' 'es2018.regexp' 'esnext.array' 'esnext.symbol' 'esnext.asynciterable' 'esnext.intl'
--allowJs Allow javascript files to be compiled.
--jsx KIND Specify JSX code generation: 'preserve', 'react-native', or 'react'.
-d, --declaration Generates corresponding '.d.ts' file.
--declarationMap Generates a sourcemap for each corresponding '.d.ts' file.
--sourceMap Generates corresponding '.map' file.
--outFile FILE Concatenate and emit output to single file.
--outDir DIRECTORY Redirect output structure to the directory.
--removeComments Do not emit comments to output.
--noEmit Do not emit outputs.
--strict Enable all strict type-checking options.
--noImplicitAny Raise error on expressions and declarations with an implied 'any' type.
--strictNullChecks Enable strict null checks.
--strictFunctionTypes Enable strict checking of function types.
--strictPropertyInitialization Enable strict checking of property initialization in classes.
--noImplicitThis Raise error on 'this' expressions with an implied 'any' type.
--alwaysStrict Parse in strict mode and emit "use strict" for each source file.
--noUnusedLocals Report errors on unused locals.
--noUnusedParameters Report errors on unused parameters.
--noImplicitReturns Report error when not all code paths in function return a value.
--noFallthroughCasesInSwitch Report errors for fallthrough cases in switch statement.
--types Type declaration files to be included in compilation.
--esModuleInterop Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.
@<file> Insert command line options and files from a file.
Compiling
Let's add to the project some TypeScript file.
..\demo-typescript\src\index.ts:
function greeter(person : string) {
return "Hello, " + person;
}
let user = "Bojan";
let greeting = greeter(user);
console.log(greeting);
return "Hello, " + person;
}
let user = "Bojan";
let greeting = greeter(user);
console.log(greeting);
Let's follow an example from tsc --help in order to get TS code compiled into JavaScript:
..\demo-typescript>npx tsc --outFile build/index.js src/index.ts
After this command is executed build/index.js file is created and it looks like this:
function greeter(person) {
return "Hello, " + person;
}
var user = "Bojan";
var greeting = greeter(user);
console.log(greeting);
return "Hello, " + person;
}
var user = "Bojan";
var greeting = greeter(user);
console.log(greeting);
We can run it with Node.js:
\demo-typescript>node build/index.js
Hello, Bojan
Hello, Bojan
It is not possible to specify compiling files from entire directory via command line arguments - all files have to be explicitly listed. This and also listing all arguments in the command line is not practical. Solution for this is specifying all options in the configuration file and instructing tsc to read everything from it.
Configuration
TypeScript transpiler uses a config file tsconfig.json. To create , use:
..\demo-typescript>npx tsc --init
message TS6071: Successfully created a tsconfig.json file.
message TS6071: Successfully created a tsconfig.json file.
---
If we installed TypeScript as a local package (and possibly as dev dependency) via:
$ npm install typescript --save-dev
...and if we added to package.json:
"scripts": {
...
"tsc": "tsc"
},
...then we can call tsc init via:
$ npm run tsc -- --init
---
Default content of tsconfig.json is:
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
If we execute
>npx tsc
...tsc will compile all .ts files in the project and place matching .js files next to .ts files.
To specify output directory (named e.g. "bundle", "dist" or "build") into which we want output structure to be placed in, we can set in config file:
"outDir": "./bundle/",
npx tsc will now output all .js files (preserving their original directory tree) in the bundle directory.
Let's now add build and start scripts to package.json so we don't have to use npx and run tools manually but follow the common approach - use npm as the task runner:
..\demo-typescript\package.json:
"scripts": {
...
"build-ts" : "tsc",
"start" : "node bundle/index.js"
},
...
"build-ts" : "tsc",
"start" : "node bundle/index.js"
},
Let's build the app:
..\demo-typescript>npm run build-ts
> demo-typescript@1.0.0 build-ts ..\demo-typescript
> tsc
Let's run the app:
..\demo-typescript>npm run start
> demo-typescript@1.0.0 start ..\demo-typescript
> node bundle/index.js
Hello, Bojan
> demo-typescript@1.0.0 build-ts ..\demo-typescript
> tsc
Let's run the app:
..\demo-typescript>npm run start
> demo-typescript@1.0.0 start ..\demo-typescript
> node bundle/index.js
Hello, Bojan
I used name build-ts for script which runs tsc but this script is usually named just tsc.
In case you get an error like this:
$ npm run tsc
...
error TS18003: No inputs were found in config file '/path/to/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '[]'.
Makes sure you have .ts (not .js!) files in the directory (or its subdirectories) where tsconfig.json resides.
In case you get an error like this:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
node_modules/@types/babel__template/index.d.ts:16:28 - error TS2583: Cannot find name 'Set'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.
16 placeholderWhitelist?: Set<string>;
~~~
Found 1 error.
...simply change tsconfig.json to contain:
"lib": ["es2015"],
If you get error like this:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
src/main.ts:1:1 - error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.
1 console.log("Hello, world")
~~~~~~~
Found 1 error.
...follow the suggestion and add 'dom' to the lib property:
"lib": ["es2015", "dom"],
Error example:
$ npm run tsc
> my_app@1.0.0 tsc /home/bojan/../my_app
> tsc
tsconfig.json:5:5 - error TS6082: Only 'amd' and 'system' modules are supported alongside --outFile.
5 "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
~~~~~~~~
tsconfig.json:13:5 - error TS6082: Only 'amd' and 'system' modules are supported alongside --outFile.
13 "outFile": "./build/wpsync.js", /* Concatenate and emit output to single file. */
~~~~~~~~~
Found 2 errors.
In "module": "commonjs", replace commonjs with system:
xxx
Module Generation
module property in tsconfig.json can be set to one of the following values: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
If we want to make tsc bundling build output into a single file we have to use outFile property but this in turn requires module to be set to either amd or system.
Asynchronous Module Definition requires RequireJS
If modules is set to amd (Asynchronous Module Definition [Wikipedia]) then our project needs to use library which implements AMD e.g. RequireJS.
This is the example of the tsc output in case when module is set to amd:
"use strict";
define("src/greeter", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function greeter(name) {
return "Hello, " + name;
}
exports.greeter = greeter;
});
define("src/main", ["require", "exports", "src/greeter"], function (require, exports, greeter_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log(greeter_1.greeter('Bojan'));
});
If we try to run it without setting RequireJS beforehand:
$ node app.js
...we'll get an error:
ReferenceError: define is not defined
Uncaught ReferenceError: define is not defined typescript
How to run a TypeScript app compiled into a single file with AMD modules?
RequireJS is module loader used mostly in web application and is not native for Node.js applications.
System requires SystemJS
If modules is set to system then our project needs to use SystemJS.
This is the example of the tsc output in case when module is set to system:
"use strict";
System.register("src/greeter", [], function (exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
function greeter(name) {
return "Hello, " + name;
}
exports_1("greeter", greeter);
return {
setters: [],
execute: function () {
}
};
});
System.register("src/main", ["src/greeter"], function (exports_2, context_2) {
"use strict";
var greeter_1;
var __moduleName = context_2 && context_2.id;
return {
setters: [
function (greeter_1_1) {
greeter_1 = greeter_1_1;
}
],
execute: function () {
console.log(greeter_1.greeter('Bojan'));
}
};
});
If we try to run it without setting SystemJS beforehand:
$ node app.js
...we'll get an error:
ReferenceError: System is not defined
How do I fix “ReferenceError: System is not defined” when running script from command line?
If we don't want to use RequireJS or SystemJS then we can't use tsc to compile our TS code into a single file bundle. We need to use some bundler...like Rollup.js. Seems that the answer How do I compile my TypeScript code for Node.js to one file? is still actual.
CommonJS
Default value for module property is commonjs. This means that - CommonJS is gonna be used and CommonJS is a "native" module loader for Node.js applications.
Syntax
Variables
const x: number = 0;
TIP: Follow const-correctness and always declare variable as const when its value is not supposed to be changed. Good practice is to make variable const instantly and change it to non-const later, when proved to be necessary.
TIP: Follow const-correctness and always declare variable as const when its value is not supposed to be changed. Good practice is to make variable const instantly and change it to non-const later, when proved to be necessary.
Functions
Function Arguments
function foo(s: string){}
function foo(n: number){}
function foo(n: number){}
Promises
This is a signature of Promise constructor. It takes a function (named executor) which itself takes two functions (callbacks), named resolve and reject:Promise
(
executor:
(
resolve: (value?: void | PromiseLike<void> | undefined) => void,
reject: (reason?: any) => void
) => void
): Promise<void>
TypeScript Deep Dive: Promise
The return type of an async function or method must be the global Promise<T> type:
- the return type of an async function is a promise
- It's like how async functions in .NET need be declared as returning a Task<T> but the actual code is written as though it returns T directly. The async and await keywords are just sugar for automatic promise wrapping/unwrapping.
- await does not make a function synchronous. From the point of view of the topmost (non-awaiting) caller it behaves like a return and the function resumes “in the background”, controlled by the event loop.
async-await idiom
Modules
.ts file becomes a module if it exports types, enums, functions, classes or interfaces.
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. [https://www.typescriptlang.org/docs/handbook/modules.html]
Export syntax:
export class MyClass {
...
}
export function MyFunction() : T {
...
}
export enum MyEnum {
...
}
...
}
export function MyFunction() : T {
...
}
export enum MyEnum {
...
}
Import syntax:
import { MyInterface, MyClass, MyFunction, MyEnum, MyType } from '../myDomain/myModule';
import { Foo } from '../dir1/index.ts'
can be replaced with shorter form:
import { Foo } from '../dir1'
provided that conditions for such module resolution are met.If you use require in your .ts code:
var config = require("./config")
...you might get the following TS compiler error:
Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.ts(2591)
This is because TS compiler knows only about "classic" browser/front-end JavaScript and require() is not part of it but is available in Node.js.
Solution
Client on node: Uncaught ReferenceError: require is not defined
const config = require('./config')
...might make tsc generate the following informational message:
'require' call may be converted to an import. ts(80005)
After conversion the code line becomes:
import config from './config';
If we had:
import config from './config/config.json';
Cannot find module './config/config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension. ts(2732)
request
The following line:const request = require('request');
makes TSLint to complain with:
require statement not part of an import statement (no-var-requires) tslint(1)
Typescript : require statement not part of an import statement
Rule: no-var-requires
If we change it to:
import request = require('request');
...then TS compiler complaints with:
Could not find a declaration file for module 'request'. '/home/bojan/dev/github/demo-typescript/node_modules/request/index.js' implicitly has an 'any' type.
Try `npm install @types/request` if it exists or add a new declaration (.d.ts) file containing `declare module 'request';`ts(7016)
Luckily, npm package we want to use does provide type definitions: https://www.npmjs.com/package/@types/request
Installing @types package makes TS error message to disappear:
$ npm install @types/request --save-dev
Read more about type definition files (.d.ts) here:
How do I add TypeScript types to a JavaScript module without switching to TypeScript?
By looking the node_modules/@types/request/index.d.ts file we can now examine the TS types in the API of that package. This is very useful as now we don't have to use any for types used in package's API (e.g. callbacks) but regular TS types.
TypeScript + Node.js + Rollup:
tsconfig.json:"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
Importing CommonJS dependencies
If I import CommonJS package e.g. request as:
import request = require("request");
This compiles with no errors but build output bundle JS file does not have any code line which performs this import and when I run the app I get runtime error:
at Object.<anonymous> (/home/bojan/dev/.../my-app/build/app.js:16:1)
at Module._compile (internal/modules/cjs/loader.js:805:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:816:10)
at Module.load (internal/modules/cjs/loader.js:672:32)
at tryModuleLoad (internal/modules/cjs/loader.js:612:12)
at Function.Module._load (internal/modules/cjs/loader.js:604:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:868:12)
at internal/main/run_main_module.js:21:11
If I have:
import request from "request";
This makes compile error:
(!) Unresolved dependencies, request (imported by index.ts)
...while transpiled output contains:
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var request = _interopDefault(require('request'));
After installing rollup-plugin-node-resolve
npm i rollup-plugin-node-resolve --save-dev
...and adding resolve() to the list of plugins in Rollup config, building errors again:
[!] Error: 'default' is not exported by node_modules/request/index.js
If I use named import:
import { request } from "request";
...the error message changes to:
[!] Error: 'request' is not exported by node_modules/request/index.js
Interfaces
What's the difference between “declare class” and “interface” in TypeScript:
interface is for when you simply want to describe the shape of an object. There's no code generation, ever, for interfaces.
TypeScript Deep Dive: Interfaces
How can I create an object based on an interface file definition in TypeScript?
Classes
If class D derives from class B, use keyword extends:
class D extends B {
...
}
If class C implements interface I, use keyword implements:...
}
class C implements I {
...
}
...
}
Type assertion
TBC...
Typescript: cast an object to other type. How? And instanceof or typeof?
Unit Testing
How to write unit testing for Angular / TypeScript for private methods with Jasmine
...but before going this way, try to redesign your modules so you do test only methods that are exported.
References:
http://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.htmlhttps://www.typescriptlang.org/docs/handbook/tsconfig-json.html
Style guide (basarat's personal)
Microsoft's TS Coding guidelines
https://www.quora.com/topic/TypeScript
Typescript. The bad, the worse, and the ugly.
JavaScript - TypeScript: Making .NET Developers Comfortable with JavaScript (2013)
Configuring TypeScript compiler
How to run typescript compiler as a package.json script without grunt or gulp?
VSCode & TypeScript: Integrate with External Tools via Tasks
How to set up a TypeScript project
No comments:
Post a Comment