Back to blog

TypeScript Best Practices Cho Code Sạch

TypeScriptJavaScriptBest Practices
TypeScript Best Practices Cho Code Sạch

TypeScript đã cách mạng hóa cách chúng ta viết ứng dụng JavaScript. Nhưng chỉ sử dụng TypeScript thôi là chưa đủ—bạn cần sử dụng nó một cách hiệu quả. Đây là một số best practice tôi đã học được qua nhiều năm.

Sử Dụng Strict Mode

Luôn bật strict mode trong tsconfig.json của bạn:

{
  "compilerOptions": {
    "strict": true
  }
}

Điều này bật một số kiểm tra quan trọng:

  • strictNullChecks - Không còn lỗi undefined is not an object
  • strictFunctionTypes - Kiểm tra type function tốt hơn
  • strictPropertyInitialization - Đảm bảo các thuộc tính class được khởi tạo

Ưu Tiên Interface Cho Object Type

Khi định nghĩa hình dạng object, ưu tiên interface hơn type alias:

// Ưu tiên cái này
interface User {
  id: string
  name: string
  email: string
}
 
// Hơn cái này
type User = {
  id: string
  name: string
  email: string
}

Interface có thể được mở rộng và merged, làm cho chúng linh hoạt hơn cho các tác giả thư viện và codebase lớn.

Sử Dụng Discriminated Union

Discriminated union hoàn hảo cho việc biểu diễn trạng thái:

type RequestState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }
 
function handleState<T>(state: RequestState<T>) {
  switch (state.status) {
    case 'idle':
      return 'Ready to fetch'
    case 'loading':
      return 'Loading...'
    case 'success':
      return `Data: ${state.data}` // TypeScript biết data tồn tại ở đây
    case 'error':
      return `Error: ${state.error.message}` // Và error tồn tại ở đây
  }
}

Tránh any, Sử Dụng unknown

Khi bạn không biết type, sử dụng unknown thay vì any:

// Không tốt - any vô hiệu hóa type checking
function processData(data: any) {
  return data.foo.bar // Không có lỗi, nhưng có thể crash tại runtime
}
 
// Tốt - unknown yêu cầu type checking
function processData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'foo' in data) {
    // Bây giờ chúng ta có thể truy cập thuộc tính an toàn
  }
}

Sử Dụng Utility Type

TypeScript cung cấp các utility type mạnh mẽ:

interface User {
  id: string
  name: string
  email: string
  password: string
}
 
// Làm tất cả thuộc tính optional
type PartialUser = Partial<User>
 
// Làm tất cả thuộc tính required
type RequiredUser = Required<User>
 
// Pick các thuộc tính cụ thể
type UserCredentials = Pick<User, 'email' | 'password'>
 
// Omit các thuộc tính cụ thể
type PublicUser = Omit<User, 'password'>
 
// Làm thuộc tính readonly
type ReadonlyUser = Readonly<User>

Generic Constraint

Sử dụng constraint để làm cho generic hữu ích hơn:

// Không có constraint - quá rộng
function getProperty<T>(obj: T, key: string) {
  return obj[key] // Lỗi: không thể index T với string
}
 
// Với constraint - type-safe
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key] // Hoạt động!
}
 
const user = { name: 'John', age: 30 }
getProperty(user, 'name') // Trả về string
getProperty(user, 'age')  // Trả về number
getProperty(user, 'foo')  // Lỗi: 'foo' không phải key của user

Kết Luận

Những thực hành này sẽ giúp bạn viết code TypeScript an toàn hơn, dễ bảo trì hơn. Điều quan trọng là tận dụng hệ thống type thay vì chống lại nó. Hãy để TypeScript phát hiện bug tại thời điểm compile để bạn có thể tập trung vào xây dựng tính năng.

Bạn thấy mô hình TypeScript nào hữu ích nhất? Hãy cho tôi biết!

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.