CI/CD for TypeScript Projects: Setting Up GitHub Actions from Scratch

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to CI/CD for TypeScript Projects



Modern software development is unthinkable without automation. CI/CD (Continuous Integration / Continuous Delivery) is a practice that allows development teams to quickly and safely deliver changes to production. For TypeScript projects, especially in the Node.js ecosystem, setting up a CI/CD pipeline becomes a critical task.



In this article, we will break down how to set up a full CI/CD pipeline for a TypeScript project using GitHub Actions. You will learn how to automate running tests, building the project, and deploying to hosting. The material is aimed at developers who are already familiar with TypeScript and want to implement modern DevOps practices.



What is GitHub Actions and why is it convenient for TypeScript



GitHub Actions is a built-in CI/CD system that allows you to create automated workflows directly in a GitHub repository. Main advantages:



  • Free tier — 2000 build minutes per month available for public repositories.
  • Integration with GitHub — no need to configure third-party services.
  • Flexibility — supports any language, including TypeScript, by running shell commands.
  • Large ecosystem — ready-made actions for deploying to AWS, Vercel, Netlify, and other platforms.


For TypeScript projects, GitHub Actions is ideal because it allows you to easily run npm or yarn commands, use dependency caching, and parallel tasks.



Basic structure of a workflow file



A workflow in GitHub Actions is described in YAML files stored in the .github/workflows/ directory. Let's look at a minimal configuration for a TypeScript project.



Create the file .github/workflows/ci.yml:



name: CI Pipeline

on: push: branches: [ main, develop ] pull_request: branches: [ main ]

jobs: build-and-test: runs-on: ubuntu-latest

strategy: matrix: node-version: [18.x, 20.x]

steps: - name: Checkout repository uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }}

- name: Install dependencies run: npm ci

- name: Lint check run: npm run lint

- name: Type check run: npx tsc --noEmit

- name: Run tests run: npm test

- name: Build project run: npm run build


Breakdown of key elements:

  • on — triggers for execution (push to main/develop, pull request).
  • runs-on — operating system for execution (ubuntu-latest).
  • matrix — parallel execution on different Node.js versions (18 and 20).
  • actions/checkout — standard action for cloning the repository.
  • actions/setup-node — installation of the specified Node.js version.
  • npm ci — clean installation of dependencies from the lock file (faster and more reliable than npm install).


Adding caching to speed up the build



To avoid downloading dependencies every time, add caching:



      - name: Cache Node.js modules        uses: actions/cache@v3        with:          path: ~/.npm          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}          restore-keys: |            ${{ runner.os }}-node-


This step caches ~/.npm based on the hash of package-lock.json. On a subsequent run, if the dependencies have not changed, the cache will be restored, saving 30-60 seconds.

Blogs

Book Recommendations