TypeScript Introduction and Environment Setup โ
What is TypeScript? โ
TypeScript is a superset of JavaScript that adds a static type system on top of JavaScript. Developed and maintained by Microsoft, TypeScript is not only compatible with all existing JavaScript code but also provides developers with powerful type checking and modern language features.
The Essence of TypeScript โ
You can understand TypeScript this way:
- JavaScript: Like having a casual conversation with friendsโfree to express, but easy to misunderstand
- TypeScript: Like signing a formal contract where every clause is clearly defined to avoid ambiguity and errors
// Pure JavaScript - Errors only discovered at runtime
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Error: item doesn't have price property
const result = calculateTotal([
{ name: "Apple", cost: 5 }, // Property name mismatch
{ name: "Banana", price: 3 },
]);
// You won't discover result is NaN until runtime!
console.log(result); // NaN// TypeScript - Errors discovered during coding
interface Product {
name: string;
price: number;
}
function calculateTotal(items: Product[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Error caught while coding!
const result = calculateTotal([
{ name: "Apple", cost: 5 }, // Error: 'cost' not in 'Product'
{ name: "Banana", price: 3 }, // Correct
]);
// Problems discovered and fixed before compilationTypeScript Advantages โ
1. Type Safety โ
Type safety is TypeScript's greatest valueโit catches a large number of potential errors during compilation.
// Basic type constraints
function processUserData(id: number, name: string, isActive: boolean) {
console.log(`User ${id}: ${name}, Status: ${isActive}`);
}
// Type checking at compile time
processUserData(123, "John Smith", true); // โ Correct
processUserData("abc", "Jane Doe", false); // โ Error: id should be number
processUserData(456, 789, true); // โ Error: name should be string2. Enhanced IDE Support โ
TypeScript provides rich type information to editors, greatly improving development experience.
interface User {
id: number;
name: string;
email: string;
role: "admin" | "user" | "guest";
lastLogin?: Date; // Optional property
}
function updateUser(user: User, updates: Partial<User>) {
return { ...user, ...updates };
}
// IDE provides:
// 1. Auto-completion
const user: User = {
id: 1,
name: "John Smith", // IDE suggests all required properties
// email: ... // IDE shows missing properties
};
// 2. Type checking and error hints
const updated = updateUser(user, {
lastLogin: new Date(), // โ Correct type
age: 30, // โ Error: 'age' not in 'User' type
});
// 3. Smart suggestions
updated.role = "admin"; // IDE shows possible values: 'admin' | 'user' | 'guest'3. Refactoring Confidence โ
With type system protection, refactoring large projects becomes much safer.
// Before refactoring
class UserService {
constructor(private database: Database) {}
async getUser(id: number): Promise<User | null> {
return this.database.users.findById(id);
}
async updateUser(id: number, data: Partial<User>): Promise<User> {
return this.database.users.update(id, data);
}
}
// Let's refactor: Transform User type to Person type
interface Person {
uuid: string; // Changed from id to uuid, type from number to string
name: string;
email: string;
role: UserRole;
lastLogin?: Date;
}
// TypeScript will flag errors everywhere User is used, ensuring no refactoring is missed
class UserService {
constructor(private database: Database) {}
async getUser(id: number): Promise<Person | null> {
// โ Error: Parameter type mismatch
return this.database.users.findById(id); // โ Error: Return type mismatch
}
async updateUser(id: number, data: Partial<Person>): Promise<Person> {
// โ Error
return this.database.users.update(id, data); // โ Error
}
}
// Safe code after refactoring
class UserService {
constructor(private database: Database) {}
async getUser(uuid: string): Promise<Person | null> {
return this.database.users.findByUuid(uuid);
}
async updateUser(uuid: string, data: Partial<Person>): Promise<Person> {
return this.database.users.updateByUuid(uuid, data);
}
}4. Better Documentation and Code Readability โ
Types themselves are the best documentation.
// Without types in JavaScript
function createOrder(data) {
// What parameters does this function accept?
// What structure should data have?
// What does it return?
}
// With types in TypeScript
interface OrderData {
customerId: number;
items: OrderItem[];
shippingAddress: Address;
paymentMethod: PaymentMethod;
}
interface OrderItem {
productId: number;
quantity: number;
unitPrice: number;
}
interface Address {
street: string;
city: string;
state: string;
zipCode: string;
country: string;
}
type PaymentMethod = "credit_card" | "debit_card" | "paypal" | "bank_transfer";
interface Order {
id: string;
customerId: number;
items: OrderItem[];
totalAmount: number;
status: "pending" | "confirmed" | "shipped" | "delivered" | "cancelled";
createdAt: Date;
estimatedDelivery: Date;
}
function createOrder(data: OrderData): Promise<Order> {
// Function signature clearly explains:
// 1. Input parameter structure and types
// 2. Return value type
// 3. All possible states and values
}TypeScript's Type System โ
1. Basic Types โ
TypeScript provides all JavaScript basic types plus additional utility types:
// Basic types
let isDone: boolean = false;
let decimal: number = 6.66;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let x: [string, number] = ["hello", 10]; // Tuple
// Special types
let u: undefined = undefined;
let n: null = null;
let any: any = 42; // Any type (use cautiously)
let unknown: unknown = 42; // Unknown type (safer than any)
let never: never = (() => {})(); // Never exists type
let void: void = undefined; // No return value
// Literal types
let direction: "north" | "south" | "east" | "west" = "north";
let status: 200 | 404 | 500 = 200;
// Object types
interface Point {
x: number;
y: number;
}
let point: Point = { x: 10, y: 20 };
// Optional properties
interface User {
id: number;
name: string;
email?: string; // Optional property
}
// Readonly properties
interface Config {
readonly host: string;
readonly port: number;
}
let config: Config = {
host: "localhost",
port: 3000
};
// config.host = "example.com"; // โ Error: host is readonly2. Function Types โ
TypeScript provides rich type definition capabilities for functions:
// Basic function types
function add(x: number, y: number): number {
return x + y;
}
// Optional and default parameters
function greet(name: string, greeting?: string = "Hello") {
return greeting + ", " + name;
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((sum, num) => sum + num, 0);
}
// Function expression types
let multiply: (x: number, y: number) => number = (x, y) => x * y;
// Async functions
async function fetchData(url: string): Promise<Response> {
const response = await fetch(url);
return response;
}
// Callback functions
function mapArray<T, U>(
array: T[],
mapper: (item: T, index: number, array: T[]) => U
): U[] {
return array.map(mapper);
}
// Usage example
const numbers = [1, 2, 3, 4, 5];
const doubled = mapArray(numbers, (n) => n * 2); // Type inferred as number[]3. Advanced Types โ
TypeScript's advanced type system provides powerful type composition capabilities:
// Union Types
type Status = "pending" | "approved" | "rejected";
type ID = string | number;
function processStatus(status: Status) {
// TypeScript knows status can only be one of these three values
switch (status) {
case "pending":
console.log("Processing...");
break;
case "approved":
console.log("Approved");
break;
case "rejected":
console.log("Rejected");
break;
}
}
// Intersection Types
interface Person {
name: string;
age: number;
}
interface Employee {
id: string;
department: string;
}
type EmployeeInfo = Person & Employee;
const emp: EmployeeInfo = {
name: "Alex Johnson",
age: 30,
id: "EMP001",
department: "Engineering",
};
// Generics
interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
}
function createApiResponse<T>(data: T, success = true): ApiResponse<T> {
return {
success,
data,
message: success ? "Operation successful" : "Operation failed",
};
}
// Using generics
const userResponse = createApiResponse({
id: 1,
name: "Sarah Williams",
}); // Type is ApiResponse<{id: number; name: string;}>
const userListResponse = createApiResponse([
{ id: 1, name: "Sarah Williams" },
{ id: 2, name: "Michael Brown" },
]); // Type is ApiResponse<{id: number; name: string;}[]>Environment Setup โ
1. Installing TypeScript โ
There are several ways to start using TypeScript:
Method 1: Global Installation โ
# Install TypeScript compiler
npm install -g typescript
# Check installed version
tsc --version
# Compile TypeScript file
tsc app.tsMethod 2: Project-level Installation (Recommended) โ
# Create project directory
mkdir my-typescript-project
cd my-typescript-project
# Initialize package.json
npm init -y
# Install TypeScript as development dependency
npm install --save-dev typescript
# Install type declarations (Node.js type definitions)
npm install --save-dev @types/node
# Initialize TypeScript configuration
npx tsc --init2. TypeScript Configuration File โ
tsc --init creates a tsconfig.json file, which is the core configuration for TypeScript projects:
{
"compilerOptions": {
/* Basic Options */
"target": "ES2020", // Compilation target version
"module": "commonjs", // Module system
"lib": ["ES2020", "DOM"], // Library files to include
"outDir": "./dist", // Output directory
"rootDir": "./src", // Source code root directory
/* Strict Checking Options */
"strict": true, // Enable all strict checks
"noImplicitAny": true, // Disallow implicit any type
"strictNullChecks": true, // Strict null checking
"strictFunctionTypes": true, // Strict function type checking
"noImplicitReturns": true, // Functions must have return value
"noFallthroughCasesInSwitch": true, // Switch statements must have break
/* Module Resolution Options */
"moduleResolution": "node", // Module resolution strategy
"baseUrl": "./", // Base path
"paths": {
// Path mapping
"@/*": ["src/*"],
"@/components/*": ["src/components/*"]
},
"allowSyntheticDefaultImports": true, // Allow synthetic default imports
/* Other Options */
"esModuleInterop": true, // Enable ES module interop
"skipLibCheck": true, // Skip library file checking
"forceConsistentCasingInFileNames": true, // Force consistent file name casing
"declaration": true, // Generate declaration files
"declarationMap": true, // Generate declaration file source maps
"sourceMap": true, // Generate source map files
"removeComments": true, // Remove comments
"noEmitOnError": true // Don't generate files on error
},
"include": [
"src/**/*" // Files to include
],
"exclude": [
"node_modules", // Files to exclude
"dist"
]
}3. Build Tool Integration โ
Integration with Vite (Modern Frontend Development Recommended) โ
# Create Vite + TypeScript project
npm create vite@latest my-app -- --template vanilla-ts
# Or React + TypeScript
npm create vite@latest my-react-app -- --template react-ts
# Or Vue + TypeScript
npm create vite@latest my-vue-app -- --template vue-tsVite projects come with TypeScript support configured. Main configuration file:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
// TypeScript configuration automatically read from tsconfig.json
});Integration with Webpack โ
# Install necessary dependencies
npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev ts-loader typescript
npm install --save-dev html-webpack-plugin// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
devServer: {
static: {
directory: path.join(__dirname, "dist"),
},
compress: true,
port: 3000,
},
};4. Editor Configuration โ
VS Code Recommended Settings โ
{
"typescript.preferences.quoteStyle": "single",
"typescript.suggest.autoImports": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.workspaceSymbols.scope": "allOpenProjects",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"typescript.preferences.importModuleSpecifier": "relative"
}Recommended VS Code Extensions โ
- TypeScript Importer: Auto-import modules
- TypeScript Hero: Type checking and refactoring tools
- Auto Rename Tag: Auto-rename tags
- Prettier: Code formatting
- ESLint: Code quality checking
First TypeScript Project โ
Let's create a simple todo application to experience TypeScript:
1. Project Structure โ
my-todo-app/
โโโ src/
โ โโโ models/
โ โ โโโ Todo.ts
โ โโโ components/
โ โ โโโ TodoItem.ts
โ โ โโโ TodoList.ts
โ โโโ services/
โ โ โโโ TodoService.ts
โ โโโ app.ts
โ โโโ index.ts
โโโ dist/
โโโ package.json
โโโ tsconfig.json2. Define Data Models โ
// src/models/Todo.ts
export interface Todo {
id: string;
title: string;
description?: string;
completed: boolean;
createdAt: Date;
updatedAt: Date;
}
export interface CreateTodoInput {
title: string;
description?: string;
}
export interface UpdateTodoInput {
title?: string;
description?: string;
completed?: boolean;
}3. Implement Service Layer โ
// src/services/TodoService.ts
import { Todo, CreateTodoInput, UpdateTodoInput } from "../models/Todo";
export class TodoService {
private todos: Todo[] = [];
getAll(): Todo[] {
return [...this.todos];
}
getById(id: string): Todo | null {
return this.todos.find((todo) => todo.id === id) || null;
}
create(input: CreateTodoInput): Todo {
const newTodo: Todo = {
id: this.generateId(),
title: input.title,
description: input.description,
completed: false,
createdAt: new Date(),
updatedAt: new Date(),
};
this.todos.push(newTodo);
return newTodo;
}
update(id: string, input: UpdateTodoInput): Todo | null {
const todoIndex = this.todos.findIndex((todo) => todo.id === id);
if (todoIndex === -1) {
return null;
}
const updatedTodo: Todo = {
...this.todos[todoIndex],
...input,
updatedAt: new Date(),
};
this.todos[todoIndex] = updatedTodo;
return updatedTodo;
}
delete(id: string): boolean {
const todoIndex = this.todos.findIndex((todo) => todo.id === id);
if (todoIndex === -1) {
return false;
}
this.todos.splice(todoIndex, 1);
return true;
}
private generateId(): string {
return Math.random().toString(36).substring(7);
}
}4. Implement Components โ
// src/components/TodoItem.ts
import { Todo } from "../models/Todo";
export class TodoItem {
private element: HTMLElement;
private todo: Todo;
private onDelete?: (id: string) => void;
private onToggle?: (id: string) => void;
constructor(
todo: Todo,
onDelete?: (id: string) => void,
onToggle?: (id: string) => void
) {
this.todo = todo;
this.onDelete = onDelete;
this.onToggle = onToggle;
this.element = this.createElement();
}
private createElement(): HTMLElement {
const div = document.createElement("div");
div.className = `todo-item ${this.todo.completed ? "completed" : ""}`;
div.innerHTML = `
<div class="todo-content">
<input type="checkbox" ${this.todo.completed ? "checked" : ""}>
<span class="todo-title">${this.todo.title}</span>
${
this.todo.description
? `<p class="todo-description">${this.todo.description}</p>`
: ""
}
</div>
<div class="todo-actions">
<button class="delete-btn">Delete</button>
</div>
`;
// Bind events
const checkbox = div.querySelector(
'input[type="checkbox"]'
) as HTMLInputElement;
const deleteBtn = div.querySelector(".delete-btn") as HTMLButtonElement;
checkbox.addEventListener("change", () => {
this.onToggle?.(this.todo.id);
});
deleteBtn.addEventListener("click", () => {
this.onDelete?.(this.todo.id);
});
return div;
}
update(todo: Todo): void {
this.todo = todo;
const newElement = this.createElement();
this.element.replaceWith(newElement);
this.element = newElement;
}
getElement(): HTMLElement {
return this.element;
}
}// src/components/TodoList.ts
import { Todo } from "../models/Todo";
import { TodoItem } from "./TodoItem";
export class TodoList {
private element: HTMLElement;
private todoItems: Map<string, TodoItem> = new Map();
constructor(
todos: Todo[],
onDelete?: (id: string) => void,
onToggle?: (id: string) => void
) {
this.element = this.createElement();
this.render(todos, onDelete, onToggle);
}
private createElement(): HTMLElement {
const div = document.createElement("div");
div.className = "todo-list";
return div;
}
private render(
todos: Todo[],
onDelete?: (id: string) => void,
onToggle?: (id: string) => void
): void {
this.element.innerHTML = "";
todos.forEach((todo) => {
const todoItem = new TodoItem(todo, onDelete, onToggle);
this.todoItems.set(todo.id, todoItem);
this.element.appendChild(todoItem.getElement());
});
}
update(todos: Todo[]): void {
this.render(todos);
}
getElement(): HTMLElement {
return this.element;
}
}5. Main Application โ
// src/app.ts
import { TodoService } from "./services/TodoService";
import { TodoList } from "./components/TodoList";
import { Todo } from "./models/Todo";
export class App {
private todoService: TodoService;
private todoList: TodoList;
private container: HTMLElement;
constructor(container: HTMLElement) {
this.container = container;
this.todoService = new TodoService();
this.todoList = new TodoList(
this.todoService.getAll(),
this.handleDelete.bind(this),
this.handleToggle.bind(this)
);
}
render(): void {
this.container.innerHTML = `
<div class="app">
<header class="app-header">
<h1>Todo List</h1>
<div class="add-todo">
<input type="text" id="todo-input" placeholder="Add new task...">
<button id="add-btn">Add</button>
</div>
</header>
<main class="app-main">
<div class="todo-list-container"></div>
</main>
</div>
`;
this.setupEventListeners();
this.renderTodoList();
}
private setupEventListeners(): void {
const input = document.getElementById("todo-input") as HTMLInputElement;
const addBtn = document.getElementById("add-btn") as HTMLButtonElement;
addBtn.addEventListener("click", () => this.handleAdd(input.value));
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
this.handleAdd(input.value);
}
});
}
private handleAdd(title: string): void {
if (!title.trim()) return;
const todo = this.todoService.create({ title: title.trim() });
this.todoList.update(this.todoService.getAll());
// Clear input
const input = document.getElementById("todo-input") as HTMLInputElement;
input.value = "";
}
private handleDelete(id: string): void {
this.todoService.delete(id);
this.todoList.update(this.todoService.getAll());
}
private handleToggle(id: string): void {
const todo = this.todoService.getById(id);
if (todo) {
this.todoService.update(id, { completed: !todo.completed });
this.todoList.update(this.todoService.getAll());
}
}
private renderTodoList(): void {
const container = this.container.querySelector(
".todo-list-container"
) as HTMLElement;
container.appendChild(this.todoList.getElement());
}
}6. Entry File โ
// src/index.ts
import { App } from "./app";
const root = document.getElementById("root");
if (root) {
const app = new App(root);
app.render();
} else {
console.error("Root element not found");
}7. HTML File โ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo Application</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="root"></div>
<script src="./dist/index.js"></script>
</body>
</html>Summary โ
TypeScript brings revolutionary improvements to JavaScript development by adding a static type system. It not only catches numerous errors during compilation but also provides better development experience, safer refactoring capabilities, and clearer code documentation.
TypeScript's Core Value โ
- Type Safety: Discover potential errors before runtime
- Better IDE Support: Auto-completion, error hints, refactoring tools
- Code Maintainability: Types as documentation improve code readability
- Modern Features: ES6+ syntax support, better module system
- Progressive Adoption: Can be gradually introduced into existing JavaScript projects
Learning Path Recommendations โ
- Basic Syntax: Master basic types, interfaces, function types
- Advanced Features: Learn generics, union types, intersection types
- Engineering Configuration: Familiarize with tsconfig.json, build tool integration
- Best Practices: Understand type design, error handling patterns
- Ecosystem Tools: Master ESLint, Prettier, testing tools
TypeScript has become the standard for modern frontend development. Mastering it not only improves development efficiency but also enables you to write more reliable and maintainable code. In the upcoming lessons, we'll dive deeper into TypeScript's various features to help you become a master of the type system.