Cơ bảnKiến thức cơ bản

Type vs Interface trong TypeScript: Khác nhau gì?

8 phút đọc4 lượt xem
#typescript#type alias#interface#frontend#javascript

Type vs Interface trong TypeScript: Khác nhau gì?

Khi mới học TypeScript, có một câu hỏi gần như ai cũng gặp:

"Dùng type hay interface? Cái nào đúng hơn?"

Câu trả lời ngắn: cả hai đều đúng, và trong nhiều trường hợp có thể hoán đổi nhau. Nhưng có những điểm khác biệt quan trọng cần biết để dùng đúng chỗ.

Interface trong TypeScript là gì?

interface User {
  name: string;
  age: number;
  email?: string;  // ? nghĩa là optional
}

const user: User = {
  name: "Nguyễn Văn An",
  age: 25
};

implements trong class

interface Animal {
  name: string;
  makeSound(): void;
}

class Dog implements Animal {
  name = "Rex";
  
  makeSound() {
    console.log("Gâu gâu!");
  }
}

Declaration Merging — chỉ interface mới có

interface User {
  name: string;
  age: number;
}

interface User {
  email: string;  // Khai báo thêm — TypeScript sẽ gộp lại
}

// Kết quả: User có đủ name, age, email
const user: User = {
  name: "An",
  age: 25,
  email: "an@example.com"
};

Type Alias trong TypeScript là gì?

type User = {
  name: string;
  age: number;
};

// Union type — chỉ type mới làm được
type Status = "active" | "inactive" | "pending";
type ID = string | number;

let status: Status = "active";
// status = "deleted";  // Lỗi!

Intersection Type

type Admin = {
  adminLevel: number;
  permissions: string[];
};

type User = {
  name: string;
  email: string;
};

type AdminUser = User & Admin;

const admin: AdminUser = {
  name: "An",
  email: "an@admin.com",
  adminLevel: 2,
  permissions: ["read", "write", "delete"]
};

So sánh type vs interface

Tính nănginterfacetype
Định nghĩa object
Declaration merging
Union type
Intersection typeextends&
class implements✅ (chỉ object type)
Primitive alias
Mapped types
Tuple type

Khi nào dùng interface? Khi nào dùng type?

Dùng interface khi:

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

interface Repository<T> {
  findById(id: number): Promise<T>;
  save(entity: T): Promise<T>;
  delete(id: number): Promise<void>;
}

Dùng type khi:

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type UserId = string | number;
type ApiError = NetworkError | ValidationError | AuthError;
type EventHandler<T> = (event: T) => void;
type Coordinate = [number, number];

Best practice trong dự án thực tế

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary" | "danger";
  disabled?: boolean;
}

function Button({ label, onClick, variant = "primary", disabled = false }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn-${variant}`}
    >
      {label}
    </button>
  );
}

TypeScript chính thức (2026) khuyến nghị: dùng interface cho object và class, dùng type cho union/intersection và primitive. Quan trọng nhất là thống nhất trong team.

Generic Types với interface và type

Generics là tính năng mạnh mẽ cho phép bạn tạo ra các kiểu dữ liệu linh hoạt, tái sử dụng được với nhiều loại dữ liệu khác nhau. Cả interface lẫn type đều hỗ trợ generics.

Generic với interface – thường dùng cho cấu trúc dữ liệu và API response:

// Định nghĩa cấu trúc response chung cho mọi API
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
  timestamp: string;
}

// Dùng với kiểu dữ liệu cụ thể
interface User {
  id: number;
  name: string;
  email: string;
}

// TypeScript tự suy ra kiểu của data là User
const response: ApiResponse<User> = {
  data: { id: 1, name: "Nguyễn Văn An", email: "an@example.com" },
  status: 200,
  message: "Thành công",
  timestamp: "2026-06-09T10:00:00Z"
};

// Generic interface cho danh sách có phân trang
interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
}

Generic với type – thường dùng cho các utility type ngắn gọn:

// Cho phép một giá trị là T hoặc null
type Nullable<T> = T | null;

// Cho phép một giá trị là T, null, hoặc undefined
type Maybe<T> = T | null | undefined;

// Tạo phiên bản optional của một object type
type PartialRecord<K extends string, V> = { [key in K]?: V };

// Ví dụ sử dụng
const userName: Nullable<string> = null;  // Hợp lệ
const userId: Maybe<number> = undefined;   // Hợp lệ

const userProfile: Nullable<User> = fetchUser(1);  // Có thể là User hoặc null nếu không tìm thấy

Kết hợp generic với interfacetype giúp code của bạn vừa an toàn kiểu dữ liệu, vừa tái sử dụng được – đặc biệt hữu ích khi xây dựng hệ thống gọi API lớn.

Ứng dụng thực tế trong dự án React + TypeScript

Dưới đây là ví dụ thực tế về một component hiển thị hồ sơ người dùng, thể hiện cách dùng interfacetype trong React một cách đúng đắn:

import { useState, useEffect } from "react";

// Định nghĩa kiểu dữ liệu
interface UserProfile {
  id: number;
  name: string;
  email: string;
  avatarUrl: string;
  role: "admin" | "user" | "moderator";
}

// Nullable cho trường hợp chưa load xong
type Nullable<T> = T | null;

// Props của component
interface UserCardProps {
  userId: number;
  onEdit?: (user: UserProfile) => void;
}

export function UserCard({ userId, onEdit }: UserCardProps) {
  // useState với kiểu rõ ràng
  const [user, setUser] = useState<Nullable<UserProfile>>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function fetchUser() {
      try {
        const res = await fetch(`/api/users/${userId}`);
        // ApiResponse generic từ ví dụ trên
        const json: ApiResponse<UserProfile> = await res.json();
        setUser(json.data);
      } catch (err) {
        setError("Không thể tải thông tin người dùng");
      } finally {
        setLoading(false);
      }
    }
    fetchUser();
  }, [userId]);

  // Typed event handler
  const handleEditClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    e.preventDefault();
    if (user && onEdit) {
      onEdit(user);
    }
  };

  if (loading) return <p>Đang tải...</p>;
  if (error) return <p className="text-red-500">{error}</p>;
  if (!user) return null;

  return (
    <div className="user-card">
      <img src={user.avatarUrl} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <span className={`role-badge role-${user.role}`}>{user.role}</span>
      {onEdit && (
        <button onClick={handleEditClick}>Chỉnh sửa</button>
      )}
    </div>
  );
}

Ví dụ trên minh họa các pattern quan trọng: typed useState giúp TypeScript cảnh báo nếu bạn set sai kiểu dữ liệu; typed event handler đảm bảo bạn xử lý đúng kiểu sự kiện; typed API response giúp IDE tự động gợi ý các thuộc tính của data.

Câu hỏi thường gặp

Có nên dùng any không? Hạn chế tối đa việc dùng any. Khi dùng any, bạn tắt hoàn toàn tính năng kiểm tra kiểu của TypeScript – mất đi toàn bộ lợi ích. Thay vào đó, dùng unknown khi thực sự không biết kiểu (buộc bạn phải kiểm tra trước khi dùng), hoặc dùng generics để giữ tính linh hoạt mà vẫn an toàn kiểu. Chỉ chấp nhận any khi tích hợp với thư viện JavaScript cũ không có type definition.

TypeScript có làm chậm development không? Ban đầu có thể cảm thấy chậm hơn vì phải khai báo kiểu. Nhưng sau giai đoạn quen thuộc (khoảng 2–4 tuần), năng suất tăng đáng kể: IDE tự gợi ý chính xác hơn, lỗi được phát hiện ngay khi code thay vì khi chạy, refactoring an toàn hơn nhiều. Trong dự án lớn nhiều người, TypeScript tiết kiệm hàng chục giờ debug mỗi tháng.

Kết luận

Quy tắc đơn giản:

  • Định nghĩa hình dạng object → dùng interface
  • Union type, literal type, kiểu phức tạp → dùng type
  • Cả hai đều được → tuân theo quy ước của team

Bạn có thể thử trực tiếp trên TypeScript Playground.

Muốn ôn lại TypeScript từ đầu? Xem bài TypeScript là gì? Tại sao nên học TypeScript năm 2026.

Về tác giả

Ảnh đại diện tác giả Kenji — họa tiết hình học

Kenji

Kỹ sư phần mềm full-stack (Web), hơn 5 năm kinh nghiệm thực tế

  • Python
  • DB
  • Hạ tầng
  • Đào tạo & cố vấn
  • AI

Làm việc cùng đồng nghiệp người Việt, tôi thấy thiếu tài liệu kỹ thuật rõ ràng bằng tiếng Việt. codeahoc là nơi tôi chia sẻ theo hướng thực tế, dễ áp dụng.

Nguyên tắc nội dung

  • Ưu tiên nguồn gốc và góc nhìn từ thực tế triển khai.
  • Nếu có sai sót, nội dung sẽ được cập nhật và sửa kịp thời.

Khóa học liên quan

The Complete JavaScript Course 2026: From Zero to Expert!

Khóa học JavaScript toàn diện nhất từ cơ bản đến nâng cao.

4.7
Xem khóa học →

React - The Complete Guide (incl. React Router & Redux)

Làm chủ React.js với các dự án thực tế, hooks, Redux.

4.6
Xem khóa học →

Node.js, Express, MongoDB & More: The Complete Bootcamp

Backend với Node.js: REST API, authentication, MongoDB.

4.7
Xem khóa học →

*Đây là liên kết liên kết (affiliate link). Chúng tôi có thể nhận hoa hồng nếu bạn mua khóa học qua liên kết này.