🗓️ Tuần 2: Làm việc với project thực tế (Node.js / Deno)
Tuần này chúng ta sẽ học cách áp dụng TypeScript vào các project thực tế, từ module system đến việc xây dựng REST API đơn giản.
11. 📦 Module system trong TypeScript
Python imports
# Python
from utils import helper_function
from models.user import User
import json
TypeScript imports/exports
// TypeScript - Named exports/imports
export function helperFunction() { /* ... */ }
export class User { /* ... */ }
import { helperFunction, User } from './utils';
import { readFile } from 'fs/promises';
// Default export/import
export default class Database { /* ... */ }
import Database from './database';
// Import everything
import * as fs from 'fs/promises';
💡 Takeaway: TS module system rõ ràng hơn Python, ít bị conflict namespace.
12. 🔃 Async/Await trong TS
// Giống Python async/await
async function fetchUser(id: number): Promise<User> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const userData = await response.json();
return userData as User;
} catch (error) {
console.error('Fetch failed:', error);
throw error;
}
}
// Promise chaining (ít dùng)
function fetchUserOldWay(id: number): Promise<User> {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(data => data as User);
}
So với Python:
async def fetch_user(id: int) -> User:
async with httpx.AsyncClient() as client:
response = await client.get(f"/api/users/{id}")
return response.json()
13. 🧾 Đọc/ghi file với Node.js
import { readFile, writeFile } from 'fs/promises';
import { existsSync } from 'fs';
// Đọc file
async function loadConfig(): Promise<Config> {
try {
const data = await readFile('config.json', 'utf8');
return JSON.parse(data) as Config;
} catch (error) {
console.error('Cannot load config:', error);
return getDefaultConfig();
}
}
// Ghi file
async function saveData(data: any[], filename: string): Promise<void> {
const jsonData = JSON.stringify(data, null, 2);
await writeFile(filename, jsonData, 'utf8');
console.log(`Saved ${data.length} records to ${filename}`);
}
// Kiểm tra file tồn tại
if (existsSync('data.csv')) {
const csvContent = await readFile('data.csv', 'utf8');
// Process CSV...
}
So với Python:
# Python
with open('config.json', 'r') as f:
config = json.load(f)
14. 🌐 Gọi HTTP API với fetch
interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async get<T>(endpoint: string): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getToken()}`
}
});
if (!response.ok) {
throw new Error(`GET ${endpoint} failed: ${response.status}`);
}
return response.json() as T;
}
async post<T>(endpoint: string, data: any): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
return response.json() as T;
}
private getToken(): string {
return process.env.API_TOKEN || '';
}
}
// Sử dụng
const api = new ApiClient('https://api.example.com');
const users = await api.get<User[]>('/users');
15. ⚙️ Giải thích tsconfig.json
{
"compilerOptions": {
// Target JavaScript version
"target": "ES2020",
// Module system
"module": "ESNext",
"moduleResolution": "node",
// Type checking
"strict": true, // Bật tất cả strict checks
"noImplicitAny": true, // Không cho phép any ngầm định
"strictNullChecks": true, // Kiểm tra null/undefined
// Output
"outDir": "./dist", // Thư mục output
"rootDir": "./src", // Thư mục source
// Advanced
"esModuleInterop": true, // Tương thích CommonJS
"skipLibCheck": true, // Skip checking .d.ts files
"forceConsistentCasingInFileNames": true
},
// Files to include/exclude
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
💡 Tip: Luôn bật strict: true
để có type safety tốt nhất.
16. 🏃 Cách chạy file .ts nhanh nhất
Option 1: ts-node (Node.js)
# Install
npm install -g ts-node typescript
# Run directly
ts-node script.ts
# With tsconfig
ts-node --project tsconfig.json script.ts
Option 2: Bun (fastest)
# Install Bun
curl -fsSL https://bun.sh/install | bash
# Run directly (no compilation needed)
bun run script.ts
Option 3: Deno (secure by default)
# Install Deno
curl -fsSL https://deno.land/x/install/install.sh | sh
# Run with permissions
deno run --allow-read --allow-write script.ts
# Or allow all
deno run --allow-all script.ts
💡 Recommendation: Dùng Bun cho development, Node.js cho production.
17. 🧪 Bài tập: Viết REST API đơn giản
Với Express (Node.js)
import express, { Request, Response } from 'express';
interface User {
id: number;
name: string;
email: string;
}
const app = express();
app.use(express.json());
let users: User[] = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// GET /users
app.get('/users', (req: Request, res: Response) => {
res.json(users);
});
// GET /users/:id
app.get('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
// POST /users
app.post('/users', (req: Request, res: Response) => {
const { name, email }: Partial<User> = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email required' });
}
const newUser: User = {
id: users.length + 1,
name,
email
};
users.push(newUser);
res.status(201).json(newUser);
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Với Deno (Modern approach)
interface User {
id: number;
name: string;
email: string;
}
const users: User[] = [
{ id: 1, name: 'Alice', email: 'alice@example.com' }
];
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
if (req.method === 'GET' && url.pathname === '/users') {
return new Response(JSON.stringify(users), {
headers: { 'Content-Type': 'application/json' }
});
}
if (req.method === 'POST' && url.pathname === '/users') {
const body = await req.json();
const newUser: User = {
id: users.length + 1,
name: body.name,
email: body.email
};
users.push(newUser);
return new Response(JSON.stringify(newUser), { status: 201 });
}
return new Response('Not Found', { status: 404 });
}
Deno.serve({ port: 3000 }, handler);
18. 📋 Cấu trúc project chuẩn trong TS
my-typescript-project/
├── src/
│ ├── controllers/
│ │ └── userController.ts
│ ├── models/
│ │ └── User.ts
│ ├── routes/
│ │ └── userRoutes.ts
│ ├── services/
│ │ └── userService.ts
│ ├── types/
│ │ └── index.ts
│ ├── utils/
│ │ └── helpers.ts
│ └── index.ts
├── tests/
│ └── user.test.ts
├── dist/ # Compiled output
├── node_modules/
├── package.json
├── tsconfig.json
├── .eslintrc.js
├── .gitignore
└── README.md
package.json mẫu
{
"name": "my-typescript-project",
"version": "1.0.0",
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "jest",
"lint": "eslint src/**/*.ts"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"@types/express": "^4.17.0",
"@types/node": "^18.0.0",
"typescript": "^5.0.0",
"ts-node": "^10.9.0"
}
}
🎯 Bài tập tuần 2
Bài 1: File Operations
Viết chương trình TypeScript đọc file CSV, parse thành array objects, và ghi ra JSON.
Bài 2: API Client
Tạo class WeatherAPI
để gọi API thời tiết và trả về dữ liệu có type.
Bài 3: Mini REST API
Mở rộng API ở bài 17 thêm PUT và DELETE endpoints.
Bài 4: Project Setup
Setup một project TypeScript hoàn chỉnh với ESLint, testing framework.
🚀 Tuần tới
👉 Tuần 3: Type nâng cao – Lập trình mạnh mẽ hơn
Tuần tới chúng ta sẽ đi sâu vào các tính năng type system nâng cao của TypeScript!
🎉 Chúc mừng bạn đã hoàn thành tuần 2!