Nếu bạn đến từ thế giới của Java hoặc C#, bạn sẽ thấy TypeScript đôi khi "kỳ lạ". Sự kỳ lạ này nằm ở cốt lõi triết lý: Structural Typing.
1. Nominal Typing là gì? (Java, C#, Swift)
Trong Java, hai class có cấu trúc giống hệt nhau nhưng tên khác nhau thì không thể thay thế cho nhau.
// Java code
class User { String name; }
class Product { String name; }
User myUser = new Product(); // LỖI BIÊN DỊCH!Hệ thống này dựa trên tên (Nominal - Name). Bạn phải khai báo tường minh mối quan hệ (Inheritance/Interface).
2. Structural Typing là gì? (TypeScript, Go)
TypeScript quan tâm đến cấu trúc (Shape) chứ không phải tên. Nếu một object trông giống một con vịt, đi như vịt và kêu như vịt, TS coi đó là con vịt.

### Lab: Thử nghiệm Duck Typing
Hãy chạy thử mã dưới đây để thấy TS chấp nhận việc gán `User` cho `Product` (và ngược lại) nếu chúng có cùng cấu trúc. Đây là điều không bao giờ xảy ra trong Java.
```typescript-sandbox:Lab: Structural Compatibility
interface User {
name: string;
}
interface Product {
name: string;
}
const user: User = { name: "Antigravity" };
const product: Product = user; // OK trong TS!
console.log("Product Name:", product.name);
// Thử thêm thuộc tính để thấy tính tương thích (Subtyping)
const extraObj = { name: "Advanced Product", price: 100 };
const simpleUser: User = extraObj; // OK vì extraObj có ít nhất thuộc tính 'name'
console.log("Simple User Name:", simpleUser.name);3. Tại sao Senior cần hiểu điều này?
Khi thiết kế Design System, Structural Typing cho phép bạn tạo ra các component cực kỳ linh hoạt.
Ví dụ về tính tương thích (Subtyping):
interface Label {
text: string;
}
function printLabel(label: Label) {
console.log(label.text);
}
const myObj = { size: 10, text: "Hello World" };
printLabel(myObj); // HỢP LỆ!myObj có nhiều thuộc tính hơn Label, nhưng vì nó có chứa ít nhất là thuộc tính text kiểu string, nó vẫn hợp lệ. Đây là nguyên lý "Ghi đè cấu trúc".
4. Opaque Types: Khi bạn muốn "giả lập" Nominal Typing
Đôi khi, Structural Typing lại quá lỏng lẻo. Ví dụ: bạn không muốn một UserId (string) bị truyền nhầm vào hàm nhận ProductId (cũng là string).
Senior TS sử dụng kỹ thuật Branding:
type UserId = string & { __brand: "User" };
type ProductId = string & { __brand: "Product" };
function getProfile(id: UserId) { ... }
const pid = "abc" as ProductId;
getProfile(pid); // LỖI! TS sẽ ngăn chặn vì brand không khớp.Tổng kết so sánh
| Đặc tính | Nominal (Java) | Structural (TypeScript) |
|---|---|---|
| Cơ sở so sánh | Tên Class/Interface | Các thuộc tính (Members) |
| Tính linh hoạt | Thấp (An toàn tuyệt đối) | Cao (Phù hợp với Dynamic JS) |
| Kế thừa | Phải khai báo implements | Tự động tương thích nếu đủ shape |
Hiểu rõ sự khác biệt này giúp bạn quyết định khi nào nên dùng interface, khi nào nên dùng type, và cách cấu trúc dữ liệu để hệ thống vừa an toàn vừa dễ mở rộng.