TypeScript: Tạo các kiểu từ các kiểu

Xin chào các nhà vô địch TypeScript tương lai! Tôi rất vui mừng được làm hướng dẫn viên của bạn trong hành trình thú vị qua thế giới các kiểu của TypeScript. Với nhiều năm kinh nghiệm làm giáo viên khoa học máy tính, tôi đã tận mắt thấy cách hiểu các kiểu có thể biến một người mới thành một phù thủy lập trình. Hãy cùng bắt đầu cuộc phiêu lưu này!

TypeScript - Creating Types from Types

Kiểu Union

Hãy tưởng tượng bạn đang ở một cửa hàng kem, và bạn có thể chọn hoặc chocolate hoặc vanilla. Trong TypeScript, chúng ta gọi loại lựa chọn này là "kiểu union". Đó là như nói, "Tôi muốn cái này hoặc cái kia."

Hãy cùng xem một đoạn mã:

type Flavor = "chocolate" | "vanilla";

let myIceCream: Flavor;
myIceCream = "chocolate"; // Điều này ổn
myIceCream = "strawberry"; // Lỗi! Kiểu '"strawberry"' không thể gán cho kiểu 'Flavor'.

Trong ví dụ này, Flavor là một kiểu union có thể là "chocolate" hoặc "vanilla". Khi chúng ta cố gắng gán "strawberry" cho myIceCream, TypeScript sẽ nói, "Dừng lại! Đó không phải là trong thực đơn!"

Kiểu union rất hữu ích khi bạn muốn giới hạn các giá trị có thể của một biến. Dưới đây là một ví dụ khác:

type Answer = "yes" | "no" | "maybe";

function respondToQuestion(answer: Answer) {
if (answer === "yes") {
console.log("Great!");
} else if (answer === "no") {
console.log("Oh no!");
} else {
console.log("I see you're undecided.");
}
}

respondToQuestion("yes"); // Làm việc tốt
respondToQuestion("perhaps"); // Lỗi! Tham số của loại '"perhaps"' không thể gán cho tham số của loại 'Answer'.

Kiểu Intersection

Bây giờ, hãy nói về các kiểu intersection. Nếu kiểu union là về "cái này hoặc cái kia", thì kiểu intersection là về "cái này và cái kia". Đó là như khi bạn đặt một combo meal tại một nhà hàng nhanh - bạn nhận được burger, khoai tây chiên và đồ uống.

Dưới đây là cách nó trông trong mã:

type Name = {
firstName: string;
lastName: string;
};

type Age = {
age: number;
};

type Person = Name & Age;

let john: Person = {
firstName: "John",
lastName: "Doe",
age: 30
};

Trong ví dụ này, Person là một kiểu intersection kết hợp NameAge. Đó là như nói, "Một người phải có tên, họ và tuổi."

Dưới đây là một ví dụ thú vị khác:

type Swimmer = {
swim: () => void;
};

type Flyer = {
fly: () => void;
};

type FlyingFish = Swimmer & Flyer;

let nemo: FlyingFish = {
swim: () => console.log("Swimming in the ocean"),
fly: () => console.log("Flying over the waves")
};

nemo.swim(); // Output: Swimming in the ocean
nemo.fly();  // Output: Flying over the waves

Kiểu Utility

TypeScript cung cấp một bộ các kiểu utility giúp chúng ta manipulates và chuyển đổi các kiểu hiện có. Chúng giống như chiếc dao đa năng của các kiểu! Hãy cùng nhìn vào một số loại phổ biến nhất:

Partial

Partial<T> làm cho tất cả các thuộc tính của một kiểu trở thành tùy chọn. Đó là như nói, "Tôi muốn tất cả những cái này, nhưng tôi không cần tất cả chúng ngay bây giờ."

interface Todo {
title: string;
description: string;
completed: boolean;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}

const myTodo = {
title: "Learn TypeScript",
description: "Study for 2 hours",
completed: false
};

const updatedTodo = updateTodo(myTodo, {
description: "Study for 3 hours"
});

console.log(updatedTodo);
// Output: { title: "Learn TypeScript", description: "Study for 3 hours", completed: false }

Pick<T, K>

Pick<T, K> xây dựng một kiểu bằng cách chọn bộ thuộc tính K từ T. Đó là như cherry-pick những quả yêu thích của bạn từ giỏ trái cây.

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

type UserSummary = Pick<User, "id" | "name">;

const userSummary: UserSummary = {
id: 1,
name: "John Doe"
};

Omit<T, K>

Omit<T, K> xây dựng một kiểu bằng cách chọn tất cả các thuộc tính từ T và sau đó loại bỏ K. Đó là ngược lại với Pick.

interface Product {
id: number;
name: string;
price: number;
description: string;
}

type ProductPreview = Omit<Product, "description">;

const productPreview: ProductPreview = {
id: 1,
name: "Cool Gadget",
price: 99.99
};

Dưới đây là bảng tóm tắt các kiểu utility này:

Kiểu Utility Mô tả
Partial Làm cho tất cả các thuộc tính trong T tùy chọn
Pick<T, K> Xây dựng một kiểu bằng cách chọn bộ thuộc tính K từ T
Omit<T, K> Xây dựng một kiểu bằng cách chọn tất cả các thuộc tính từ T và sau đó loại bỏ K

Operator typeof

Operator typeof trong TypeScript cho phép chúng ta tạo một kiểu dựa trên hình dạng của một giá trị hiện có. Đó là như lấy một mẫu của một đối tượng để tạo ra nhiều đối tượng với cùng hình dạng.

Hãy cùng nhìn vào một ví dụ:

const user = {
name: "John",
age: 30,
location: "New York"
};

type User = typeof user;

const anotherUser: User = {
name: "Jane",
age: 25,
location: "London"
};

Trong ví dụ này, chúng ta sử dụng typeof user để tạo một kiểu mới User khớp với hình dạng của đối tượng user. Điều này rất hữu ích khi bạn muốn đảm bảo rằng nhiều đối tượng có cùng cấu trúc.

Dưới đây là một ví dụ khác nơi typeof rất hữu ích:

const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number];

let myColor: Color = "red"; // OK
myColor = "yellow"; // Lỗi: Kiểu '"yellow"' không thể gán cho kiểu '"red" | "green" | "blue"'.

Trong trường hợp này, chúng ta sử dụng typeof cùng với các kiểu truy cập chỉ mục để tạo một kiểu union từ một mảng các hằng số.

Và đó là tất cả, các bạn! Chúng ta đã cùng nhau hành trình qua thế giới các kiểu của TypeScript, tạo ra các kiểu mới từ các kiểu hiện có. Nhớ rằng, thực hành là cách tốt nhất để trở nên hoàn hảo, vì vậy đừng ngại thử nghiệm các khái niệm này trong các dự án của riêng bạn. Chúc các bạn may mắn và các kiểu luôn ở bên bạn!

Credits: Image by storyset