Table of Contents
- Prerequisites
- Installing TypeScript
- Core TypeScript Concepts
- Setting Up a TypeScript Project
- Writing Your First TypeScript Program
- Compiling TypeScript to JavaScript
- Deploying a TypeScript Project
- Troubleshooting Common Issues
- Conclusion
- References
Prerequisites
Before we start, ensure you have the following installed:
- Node.js and npm: TypeScript relies on Node.js for package management and running the compiler. Download Node.js from nodejs.org (LTS version recommended). npm (Node Package Manager) is included with Node.js.
- A Code Editor: We recommend VS Code (with the TypeScript extension for syntax highlighting and IntelliSense).
Installing TypeScript
TypeScript is distributed as an npm package, so we’ll install it using npm.
Step 1: Verify Node.js and npm Installation
First, check if Node.js and npm are installed:
node -v # Should output a version (e.g., v20.10.0)
npm -v # Should output a version (e.g., 10.2.3)
Step 2: Install TypeScript Globally or Locally
-
Global Installation (recommended for beginners): Installs the TypeScript compiler (
tsc) system-wide, so you can use it in any project.npm install -g typescript -
Local Installation (better for project-specific versions): Installs TypeScript only in your current project. Use this if you need different TypeScript versions across projects.
npm install typescript --save-dev # Adds to devDependencies
Step 3: Verify Installation
Check if tsc is working:
tsc -v # Should output the TypeScript version (e.g., Version 5.2.2)
Core TypeScript Concepts
Let’s explore the building blocks of TypeScript. These concepts will help you write type-safe code.
Basic Types
TypeScript extends JavaScript with several primitive and advanced types. Here are the most common:
| Type | Description | Example |
|---|---|---|
string | Textual data (enclosed in quotes). | "Hello", 'TypeScript' |
number | Numeric values (integers, floats, NaN, Infinity). | 42, 3.14, -10 |
boolean | true or false. | isActive: boolean = true |
null | Represents a null value (explicitly “nothing”). | let x: null = null |
undefined | Represents an uninitialized variable. | let y: undefined = undefined |
any | Opts out of type checking (use sparingly!). | let z: any = "I can be anything" |
unknown | Safer alternative to any (requires type checking before use). | let data: unknown = fetchData() |
never | Represents values that never occur (e.g., functions that throw errors). | function error(): never { throw new Error() } |
Type Annotations
Type annotations tell TypeScript the expected type of a variable, function parameter, or return value. They’re optional (TypeScript can often infer types), but explicit annotations make code clearer.
Example: Variable Annotations
let userName: string = "Alice"; // Explicit string type
let age: number = 30; // Explicit number type
let isStudent: boolean = true; // Explicit boolean type
// TypeScript infers the type if you initialize the variable:
let message = "Hello, TypeScript!"; // Inferred as string
message = 42; // ❌ Error: Type 'number' is not assignable to type 'string'
Functions
TypeScript lets you annotate function parameters and return types, ensuring functions are called with the right arguments and return the expected data.
Example: Function with Annotations
// Parameter types: name (string), age (number)
// Return type: string
function greet(name: string, age: number): string {
return `Hello, ${name}! You are ${age} years old.`;
}
// Correct usage
console.log(greet("Bob", 25)); // ✅ Output: "Hello, Bob! You are 25 years old."
// Incorrect usage (TypeScript catches the error)
console.log(greet(25, "Bob")); // ❌ Error: Argument of type 'number' is not assignable to parameter of type 'string'
Optional Parameters: Add ? to make a parameter optional.
function greetOptional(name: string, age?: number): string {
if (age) {
return `Hello, ${name}! You are ${age} years old.`;
}
return `Hello, ${name}!`;
}
greetOptional("Charlie"); // ✅ Works (age is optional)
Default Parameters: Assign a default value to parameters.
function greetDefault(name: string, age: number = 18): string {
return `Hello, ${name}! You are ${age} years old.`;
}
greetDefault("Diana"); // ✅ Uses default age: 18
Interfaces and Type Aliases
Both interface and type let you define custom types for objects, but they have subtle differences. Use interface for defining object shapes (and extend them later), and type for more flexible type definitions (e.g., unions, tuples).
Example: Interface
interface User {
id: number;
name: string;
email: string;
isAdmin?: boolean; // Optional property
}
// Use the interface to type an object
const user1: User = {
id: 1,
name: "Eve",
email: "[email protected]"
};
// Extending an interface (adds new properties)
interface AdminUser extends User {
permissions: string[];
}
const admin: AdminUser = {
id: 2,
name: "Frank",
email: "[email protected]",
permissions: ["delete", "edit"]
};
Example: Type Alias
type Point = {
x: number;
y: number;
};
type Status = "active" | "inactive" | "pending"; // Union type
const point: Point = { x: 10, y: 20 };
const userStatus: Status = "active"; // ✅ Only allows "active", "inactive", or "pending"
Classes
TypeScript enhances JavaScript classes with access modifiers (public, private, protected) and type annotations, making OOP (Object-Oriented Programming) more robust.
Example: Class with Access Modifiers
class Animal {
private name: string; // Only accessible within the class
public age: number; // Accessible anywhere (default if no modifier)
protected species: string; // Accessible within the class and subclasses
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
// Method with return type annotation
makeSound(): string {
return `${this.name} makes a sound.`;
}
}
const dog = new Animal("Buddy", 3, "Dog");
console.log(dog.age); // ✅ Public: 3
console.log(dog.name); // ❌ Error: Property 'name' is private and only accessible within class 'Animal'
Generics (Optional)
Generics let you write reusable components that work with multiple types. Think of them as “type variables” for functions or classes.
Example: Generic Function
// A function that returns the first element of an array
function getFirstElement<T>(array: T[]): T {
return array[0];
}
// Works with numbers
const numbers = [1, 2, 3];
const firstNumber = getFirstElement(numbers); // Inferred as number
// Works with strings
const fruits = ["apple", "banana"];
const firstFruit = getFirstElement(fruits); // Inferred as string
Setting Up a TypeScript Project
Now that you understand the basics, let’s set up a real project.
Initializing a Project
-
Create a new folder for your project (e.g.,
my-ts-project) and navigate to it in the terminal:mkdir my-ts-project && cd my-ts-project -
Initialize a Node.js project (this creates a
package.jsonfile):npm init -y # -y skips prompts and uses defaults
Configuring tsconfig.json
The tsconfig.json file tells the TypeScript compiler how to behave. It specifies settings like the target JavaScript version, output directory, and strictness rules.
-
Generate a
tsconfig.jsonfile:tsc --init # Creates a default tsconfig.json with comments -
Open
tsconfig.jsonin your editor and update these key settings (remove comments for clarity):{ "compilerOptions": { "target": "ES6", // Compile to ES6 JavaScript (widely supported) "module": "CommonJS", // Use CommonJS modules (Node.js default) "outDir": "./dist", // Output compiled JS files to ./dist "rootDir": "./src", // Source TS files live in ./src "strict": true, // Enables strict type-checking (RECOMMENDED!) "esModuleInterop": true, // Improves interoperability with ES modules "skipLibCheck": true, // Skips type-checking of library files "forceConsistentCasingInFileNames": true // Ensures consistent file name casing }, "include": ["src/**/*"], // Compile all TS files in ./src "exclude": ["node_modules"] // Ignore node_modules }
Key Options Explained:
strict: true: Enables rules likenoImplicitAny(prevents untyped variables) andstrictNullChecks(requires explicit handling ofnull/undefined). This is critical for catching bugs early!outDirandrootDir: Keep your source (TS) and output (JS) files organized.
Writing Your First TypeScript Program
Let’s create a simple program to test our setup.
-
Create a
srcfolder (as specified inrootDir) and add amain.tsfile inside:mkdir src && touch src/main.ts -
Open
src/main.tsand add this code (we’ll use an interface and a function):// Define an interface for a User interface User { name: string; age: number; hobbies?: string[]; // Optional array of hobbies } // Function to greet a user function greetUser(user: User): string { const hobbies = user.hobbies ? ` and likes ${user.hobbies.join(", ")}` : ""; return `Hello, ${user.name}! You are ${user.age} years old${hobbies}.`; } // Create a user object const myUser: User = { name: "Alice", age: 30, hobbies: ["reading", "hiking"] }; // Call the function and log the result console.log(greetUser(myUser)); // Output: Hello, Alice! You are 30 years old and likes reading, hiking.
Compiling TypeScript to JavaScript
TypeScript can’t run directly in browsers or Node.js—it must be compiled to JavaScript first using tsc.
Using tsc (TypeScript Compiler)
Run the compiler to convert main.ts to main.js:
tsc # Reads tsconfig.json and compiles all included TS files
Check the dist folder—you’ll see a main.js file with the compiled JavaScript:
// dist/main.js (simplified output)
"use strict";
function greetUser(user) {
const hobbies = user.hobbies ? ` and likes ${user.hobbies.join(", ")}` : "";
return `Hello, ${user.name}! You are ${user.age} years old${hobbies}.`;
}
const myUser = {
name: "Alice",
age: 30,
hobbies: ["reading", "hiking"]
};
console.log(greetUser(myUser));
Watch Mode
Manually running tsc every time you change code is tedious. Use watch mode to auto-recompile when files update:
tsc --watch # or tsc -w
Now, edit src/main.ts (e.g., change name: "Alice" to name: "Bob"), and tsc will recompile automatically.
NPM Scripts for Convenience
Add scripts to package.json to simplify common tasks like building and watching:
{
"scripts": {
"build": "tsc", // Compile once
"watch": "tsc --watch", // Auto-recompile on changes
"start": "node dist/main.js" // Run the compiled JS
}
}
Now you can run:
npm run buildto compilenpm run watchfor watch modenpm startto run the program
Deploying a TypeScript Project
Once your code is ready, you’ll need to deploy it. The process depends on your project type (Node.js app, static website, etc.). Here are common approaches:
Option 1: Deploy Compiled JavaScript
For Node.js apps or simple scripts:
- Compile your TS code to JS with
npm run build. - Deploy the
distfolder (andpackage.json) to your server or platform (e.g., AWS EC2, Heroku, or DigitalOcean).
Example for Heroku:
- Create a
Procfilein your project root:web: node dist/main.js - Push your code to Heroku (ensure
distis included in your Git repo, or runnpm run buildin Heroku’s deployment pipeline).
Option 2: Bundling with Webpack/Vite
For frontend projects (e.g., React, Vue), use bundlers like Webpack or Vite to bundle TS/JS/CSS into optimized files.
Vite Setup (Simpler for Beginners):
- Create a Vite project with TypeScript:
npm create vite@latest my-vite-ts-project -- --template vanilla-ts cd my-vite-ts-project npm install - Write your TS code in
src/main.ts. - Build for production:
npm run build # Outputs to ./dist - Deploy the
distfolder to a static host (e.g., Netlify, Vercel).
Deploying to Platforms (Vercel/Netlify)
For static sites (compiled JS/CSS/HTML), platforms like Vercel and Netlify make deployment effortless:
- Push your code to GitHub.
- Connect your repo to Vercel/Netlify.
- Configure the build command (e.g.,
npm run build) and output directory (e.g.,dist). - Deploy! Both platforms auto-deploy when you push to GitHub.
Troubleshooting Common Issues
Even with TypeScript’s help, you might run into errors. Here are fixes for common problems:
1. “Cannot find name ‘x’”
Cause: TypeScript doesn’t recognize a variable/function.
Fix: Ensure the variable is declared and typed, or check for typos.
2. “Type ‘undefined’ is not assignable to type ‘string’”
Cause: A variable might be undefined (e.g., optional property).
Fix: Use the non-null assertion operator (!) if you’re sure it’s defined:
const userName: string | undefined = "Alice";
console.log(userName!.toUpperCase()); // ✅ "ALICE" (use cautiously!)
3. tsconfig.json Not Working
Cause: Invalid syntax or missing options.
Fix: Validate with tsc --showConfig to see the resolved config, or delete tsconfig.json and regenerate with tsc --init.
Conclusion
You’ve now learned the basics of TypeScript: installing it, using types, setting up a project, compiling to JavaScript, and deploying your work. TypeScript’s type system might feel restrictive at first, but it’s a powerful tool for writing reliable code.
To grow further:
- Practice by converting small JavaScript projects to TypeScript.
- Explore advanced topics like utility types (
Partial,Readonly), decorators, and module federation. - Check out the TypeScript Handbook for deep dives.
Happy coding! 🚀