Week 3 - TypeScript

Type System

Interfaces vs Types

Generics

Type inference

Type Unions and Intersections

Function Types and Return Types

Utility Types

Type-Safe API Responses

Type Guards

HTML Element Types

Tooling

Practice

Assignment

Front end Track

TypeScript Tooling

Running node example.ts is fine for learning and quick experiments. But for real projects you need proper tooling: a compiler that type-checks and transpiles your code, and a linter that catches style and correctness issues.

This document covers setting up a TypeScript project with the TypeScript compiler (tsc) and optionally adding ESLint.


The TypeScript Compiler (tsc)

The TypeScript compiler does two independent things:

  1. Type checking — analyses your code and reports type errors.
  2. Transpilation — strips the type annotations and emits plain JavaScript.

These steps are independent. A file with type errors still produces runnable JavaScript — tsc reports the errors but emits the output anyway by default. This surprises many newcomers: type errors are warnings about correctness, not syntax errors that prevent execution. You can change this with the noEmitOnError option in tsconfig.json.

Installation

npm init -y
npm install typescript

Initialize tsconfig.json

npx tsc --init

This creates a tsconfig.json file with many options (most commented out). Here's a minimal starting point:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

<aside> ⚠️

Make sure your package.json includes "type": "module" so Node.js uses ES modules.

</aside>

What these options mean:

Option What it does
target Which JavaScript version to output. es2022 is a safe modern default.
module Module system for the output. nodenext for Node.js ES module projects.
moduleResolution How TypeScript finds imported modules. Must match module.
outDir Where compiled .js files go. Keeps source and output separate.
rootDir Where your .ts source files live.
strict Enables all strict type-checking options. Always use this.
esModuleInterop Lets you import express from 'express' instead of import * as express.
skipLibCheck Skips type-checking .d.ts files from node_modules. Speeds up compilation.
forceConsistentCasingInFileNames Prevents issues on case-insensitive file systems.

Project Structure

my-project/
├── src/
│   └── index.ts
├── dist/           ← generated by tsc, add to .gitignore
├── tsconfig.json
└── package.json

Compile and Run

npx tsc                    # compile all .ts files in src/ → dist/
node dist/index.js         # run the compiled JavaScript

npx tsc --watch            # recompile automatically on file changes

Add Scripts to package.json

{
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch"
  }
}

What Does "Removing Types" Mean?

All the tools above do the same fundamental thing: they remove type annotations so that a JavaScript engine can execute the code. The annotations are only used during development — they help your editor and the compiler catch mistakes, but they are not part of the JavaScript language.

Here's a TypeScript function:

function greet(name: string, age: number): string {
  return `Hello, ${name}. You are ${age} years old.`;
}

const message: string = greet('Aisha', 27);

After removing the types, you get plain JavaScript:

function greet(name, age) {
  return `Hello, ${name}. You are ${age} years old.`;
}

const message = greet('Aisha', 27);

Every : string and : number annotation is gone. The logic is identical — the types were only there to help you write correct code.

You can see this in action in the TypeScript Playground — paste any TypeScript on the left and the compiled JavaScript appears on the right.

How each tool handles this removal differs: tsc parses and type-checks first, then emits JavaScript. tsx uses esbuild to strip types at high speed without checking them. Node.js native support replaces type annotations with whitespace in-place[^1], so line numbers stay the same without needing source maps.

[^1]: This is why node example.ts works even though Node.js only runs JavaScript — by the time the code reaches the JavaScript engine, the types are already gone.


Optional: ESLint for TypeScript

ESLint catches bugs, enforces consistency, and flags patterns that TypeScript's type checker doesn't cover — things like unused variables, unreachable code, or inconsistent naming.

Installation

Install ESLint and the TypeScript ESLint plugin:

npm install eslint @eslint/js typescript-eslint

Configuration

Create an eslint.config.js file in your project root (ESLint v9+ uses flat config):

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  {
    ignores: ['dist/'],
  },
);

This gives you a solid set of rules out of the box, including TypeScript-specific rules like catching unused variables, preferring const, and flagging unsafe any usage.

Stricter Rules

The recommended config is a good starting point. When you're ready for stricter checks, typescript-eslint offers strict and strictTypeChecked configs that catch more issues — like unhandled promises, unsafe type assertions, and unnecessary conditions. See the typescript-eslint docs for details.

Running ESLint

Add a script to package.json:

{
  "scripts": {
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix"
  }
}

Run it:

npm run lint           # report issues
npm run lint:fix       # auto-fix what can be fixed

VS Code Integration

Install the ESLint extension for real-time linting in your editor. Errors and warnings appear as squiggles, just like TypeScript's own type errors.


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0

CC BY-NC-SA 4.0 Icons

*https://hackyourfuture.net/*

Found a mistake or have a suggestion? Let us know in the feedback form.