TypeScript NPM Scripts
NPM scripts provide a powerful way to automate your TypeScript development workflow. Instead of typing complex terminal commands repeatedly, you can define custom scripts in your package.json
file that handle tasks like compilation, testing, linting, and deployment.
Introduction to NPM Scripts
NPM scripts are defined in the "scripts"
section of your package.json
file. They allow you to create shortcuts for common tasks in your TypeScript projects, making your development process more efficient and consistent.
Think of NPM scripts as aliases for the commands you frequently use in your terminal. When you run npm run <script-name>
, NPM executes the corresponding command defined in your package.json
.
Basic TypeScript Scripts
Let's start with some essential scripts for TypeScript development:
Compilation Script
The most basic script you'll need compiles your TypeScript code to JavaScript:
{
"scripts": {
"build": "tsc"
}
}
Running npm run build
will execute the TypeScript compiler (tsc
) using your tsconfig.json
configuration.
Watch Mode
During development, you'll want the compiler to recompile whenever files change:
{
"scripts": {
"build": "tsc",
"watch": "tsc --watch"
}
}
With this script, running npm run watch
starts the TypeScript compiler in watch mode, automatically recompiling when you make changes.
Advanced NPM Scripts for TypeScript
Let's explore more sophisticated ways to use NPM scripts in your TypeScript projects.
Using Multiple Commands in a Single Script
You can run multiple commands in a single script using &&
(runs sequentially) or &
(runs in parallel):
{
"scripts": {
"clean-build": "rimraf ./dist && tsc",
"start-dev": "tsc --watch & node ./dist/server.js"
}
}
The clean-build
script first removes the existing dist
directory (using rimraf, which you'd need to install) and then compiles your TypeScript code.
Pre and Post Hooks
NPM automatically recognizes scripts named pre<script>
and post<script>
and runs them before and after the main script:
{
"scripts": {
"prebuild": "npm run clean",
"build": "tsc",
"postbuild": "echo 'Build completed successfully!'",
"clean": "rimraf ./dist"
}
}
When you run npm run build
:
prebuild
runs first, which executes theclean
script to delete thedist
directorybuild
runs next, compiling the TypeScript codepostbuild
runs last, displaying a success message
Cross-platform Compatibility
To ensure your scripts work across different operating systems (Windows, macOS, Linux), you can use cross-platform tools:
{
"scripts": {
"clean": "rimraf ./dist",
"copy-assets": "copyfiles -u 1 src/**/*.html src/**/*.css dist/",
"start": "cross-env NODE_ENV=production node dist/index.js"
},
"devDependencies": {
"rimraf": "^3.0.2",
"copyfiles": "^2.4.1",
"cross-env": "^7.0.3"
}
}
In this example:
rimraf
is a cross-platform tool for deleting directoriescopyfiles
helps copy files from source to destinationcross-env
sets environment variables in a cross-platform way
Practical Examples
Let's look at some real-world examples of NPM scripts for TypeScript projects.
Basic Web Application
{
"scripts": {
"clean": "rimraf ./dist",
"build": "tsc",
"lint": "eslint . --ext .ts",
"test": "jest",
"dev": "ts-node-dev --respawn src/index.ts",
"start": "node dist/index.js"
}
}
This setup provides scripts for:
- Cleaning the output directory
- Building the TypeScript code
- Linting the code with ESLint
- Running tests with Jest
- Development with automatic restarts using ts-node-dev
- Starting the production build
Full-Stack TypeScript Application
{
"scripts": {
"build:client": "cd client && npm run build",
"build:server": "cd server && npm run build",
"build": "npm run build:client && npm run build:server",
"dev:client": "cd client && npm run dev",
"dev:server": "cd server && npm run dev",
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
"test:client": "cd client && npm test",
"test:server": "cd server && npm test",
"test": "npm run test:client && npm run test:server"
},
"devDependencies": {
"concurrently": "^7.0.0"
}
}
This example shows how to manage a project with separate client and server directories, using the concurrently
package to run both in development mode simultaneously.
TypeScript Library with Build Process
{
"scripts": {
"clean": "rimraf ./dist",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "babel src --out-dir dist --extensions \".ts,.tsx\" --source-maps inline",
"build": "npm run clean && npm run build:types && npm run build:js",
"prepublishOnly": "npm run build",
"test": "jest",
"docs": "typedoc --out docs src"
}
}
This setup is ideal for a TypeScript library that:
- Generates declaration files separately from compiled JavaScript
- Uses Babel for transpilation
- Automatically builds before publishing
- Generates documentation with TypeDoc
Optimizing Your TypeScript Build Process
Let's look at how to optimize the build process using NPM scripts:
Parallel Builds
For large projects, running processes in parallel can save time:
{
"scripts": {
"build:client": "tsc -p tsconfig.client.json",
"build:server": "tsc -p tsconfig.server.json",
"build": "concurrently \"npm run build:client\" \"npm run build:server\""
}
}
This example uses different TypeScript configurations for client and server code and builds them concurrently.
Incremental Builds
TypeScript's incremental compilation can significantly speed up rebuild times:
{
"scripts": {
"build": "tsc --incremental"
}
}
This generates a .tsbuildinfo
file that helps TypeScript track which files need recompilation.
Project References
For complex projects, TypeScript's project references can organize and optimize compilation:
{
"scripts": {
"build": "tsc --build",
"clean": "tsc --build --clean"
}
}
This works with a tsconfig.json
that defines project references:
{
"references": [
{ "path": "./packages/common" },
{ "path": "./packages/server" },
{ "path": "./packages/client" }
],
"files": []
}
NPM Scripts Workflow Diagram
Here's a visualization of a typical TypeScript development workflow using NPM scripts:
Tips and Best Practices
-
Document your scripts: Add comments in your
package.json
or create a separate documentation file explaining what each script does. -
Use meaningful script names: Choose names that clearly indicate the purpose, like
build:prod
instead of justb
. -
Keep scripts focused: Each script should do one thing well; use script composition for complex operations.
-
Provide shorthand aliases: For frequently used complex scripts, create shorter aliases:
{
"scripts": {
"dev:full": "concurrently \"npm run watch:ts\" \"npm run watch:assets\" \"npm run serve\"",
"dev": "npm run dev:full"
}
}
- Leverage third-party tools: Tools like
npm-run-all
can simplify running multiple scripts:
{
"scripts": {
"clean": "rimraf dist",
"build:css": "sass src/styles:dist/styles",
"build:ts": "tsc",
"build": "npm-run-all clean --parallel build:*"
},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
Summary
NPM scripts provide a powerful way to automate and standardize your TypeScript development workflow. They help you:
- Simplify complex command sequences
- Create consistent development processes
- Automate repetitive tasks
- Document your project's build and development procedures
- Support cross-platform development
By learning to leverage NPM scripts effectively, you can significantly improve your productivity and make your TypeScript projects more maintainable.
Additional Resources
Exercises
- Create a TypeScript project with NPM scripts for building, testing, and linting.
- Set up pre and post hooks to display timing information for your build process.
- Create an optimized build script that cleans the output directory, compiles TypeScript, and minifies the resulting JavaScript.
- Implement a development script that watches for changes and automatically restarts your application.
- Set up NPM scripts for a full-stack TypeScript application with separate client and server builds.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)