TypeScript - Loại ánh xạ: Hướng dẫn cho người mới bắt đầu

Xin chào các pháp sư TypeScript tương lai! Hôm nay, chúng ta sẽ bắt đầu một chuyến hành trình đầy thú vị vào thế giới của các Loại ánh xạ. Đừng lo lắng nếu bạn mới bắt đầu học lập trình - tôi sẽ là người hướng dẫn thân thiện của bạn, và chúng ta sẽ cùng nhau bước từng bước. Cuối cùng của bài hướng dẫn này, bạn sẽ ánh xạ các loại như một chuyên gia!

TypeScript - Mapped Types

Loại ánh xạ là gì?

Trước khi chúng ta đi sâu vào, hãy hiểu xem Loại ánh xạ là gì. Hãy tưởng tượng bạn có một hộp sô-cô-la hỗn hợp, và bạn muốn tạo ra một hộp mới nơi mỗi viên sô-cô-la đều được bọc trong giấy nhôm vàng. Đó chính là điều mà các Loại ánh xạ làm trong TypeScript - chúng lấy một loại đã tồn tại và chuyển đổi nó thành một loại mới dựa trên một bộ quy tắc.

Các loại ánh xạ内置

TypeScript có một số Loại ánh xạ đã định nghĩa trước và rất hữu ích. Hãy cùng nhìn qua chúng một lần một:

1. Partial<T>

Loại Partial<T> làm cho tất cả các thuộc tính của T trở thành tùy chọn. Nó giống như biến một công thức nghiêm ngặt thành một công thức linh hoạt hơn, nơi bạn có thể bỏ qua một số nguyên liệu.

interface Recipe {
name: string;
ingredients: string[];
cookingTime: number;
}

type FlexibleRecipe = Partial<Recipe>;

// Bây giờ chúng ta có thể tạo một công thức mà không cần tất cả các thuộc tính
const quickSnack: FlexibleRecipe = {
name: "Toast",
// Chúng ta có thể bỏ qua ingredients và cookingTime
};

Trong ví dụ này, FlexibleRecipe cho phép chúng ta tạo một công thức mà không cần xác định tất cả các thuộc tính. Nó rất phù hợp khi bạn muốn cập nhật chỉ một phần của một đối tượng.

2. Required<T>

Required<T> là ngược lại với Partial<T>. Nó làm cho tất cả các thuộc tính trở thành bắt buộc, ngay cả khi chúng là tùy chọn trong loại gốc.

interface UserProfile {
name: string;
age?: number;
email?: string;
}

type CompleteUserProfile = Required<UserProfile>;

// Bây giờ chúng ta phải cung cấp tất cả các thuộc tính
const user: CompleteUserProfile = {
name: "Alice",
age: 30,
email: "[email protected]"
};

Ở đây, CompleteUserProfile đảm bảo rằng chúng ta cung cấp tất cả các thuộc tính, bao gồm ageemail mà là tùy chọn trong UserProfile gốc.

3. Readonly<T>

Readonly<T> làm cho tất cả các thuộc tính của T trở thành chỉ đọc. Nó giống như đặt loại của bạn trong một kính bảo vệ - bạn có thể nhìn, nhưng không thể chạm!

interface Toy {
name: string;
price: number;
}

type CollectibleToy = Readonly<Toy>;

const actionFigure: CollectibleToy = {
name: "Superhero",
price: 19.99
};

// Điều này sẽ gây ra lỗi
// actionFigure.price = 29.99;

Trong ví dụ này, một khi chúng ta tạo ra actionFigure, chúng ta không thể thay đổi các thuộc tính của nó. Điều này rất tốt để tạo ra các đối tượng không thể thay đổi.

4. Pick<T, K>

Pick<T, K> tạo ra một loại mới bằng cách chọn chỉ các thuộc tính K từ T. Nó giống như chọn những đặc điểm yêu thích của bạn từ một loại.

interface Smartphone {
brand: string;
model: string;
year: number;
color: string;
price: number;
}

type BasicPhoneInfo = Pick<Smartphone, 'brand' | 'model'>;

const myPhone: BasicPhoneInfo = {
brand: "TechBrand",
model: "X2000"
};

Ở đây, BasicPhoneInfo chỉ bao gồm các thuộc tính brandmodel từ Smartphone, bỏ qua các thuộc tính khác.

5. Omit<T, K>

Omit<T, K> là ngược lại với Pick<T, K>. Nó tạo ra một loại mới bằng cách loại bỏ các thuộc tính K từ T.

interface Book {
title: string;
author: string;
pages: number;
isbn: string;
}

type BookPreview = Omit<Book, 'pages' | 'isbn'>;

const preview: BookPreview = {
title: "TypeScript Adventures",
author: "Code Wizard"
};

Trong ví dụ này, BookPreview bao gồm tất cả các thuộc tính của Book ngoại trừ pagesisbn.

Ví dụ về việc sử dụng các loại ánh xạ

Bây giờ chúng ta đã xem xét các Loại ánh xạ内置, hãy cùng nhìn qua một số ví dụ thực tế về cách chúng ta có thể sử dụng chúng trong các tình huống thực tế.

Ví dụ 1: Tạo trạng thái biểu mẫu

Hãy tưởng tượng bạn đang xây dựng một biểu mẫu, và bạn muốn theo dõi các trường nào đã được sửa đổi:

interface LoginForm {
username: string;
password: string;
rememberMe: boolean;
}

type FormTouched = { [K in keyof LoginForm]: boolean };

const touchedFields: FormTouched = {
username: true,
password: false,
rememberMe: true
};

Ở đây, chúng ta đã tạo ra một loại FormTouched có cùng các khóa với LoginForm, nhưng tất cả các giá trị đều là boolean chỉ ra xem trường đó có bị触摸 không.

Ví dụ 2: Bọc phản hồi API

Hãy tưởng tượng bạn có một API trả về các loại dữ liệu khác nhau, và bạn muốn bọc mỗi phản hồi trong một định dạng chuẩn:

interface ApiResponse<T> {
data: T;
status: 'success' | 'error';
timestamp: number;
}

type UserData = { id: number; name: string; email: string };
type ProductData = { id: number; name: string; price: number };

const userResponse: ApiResponse<UserData> = {
data: { id: 1, name: "John Doe", email: "[email protected]" },
status: 'success',
timestamp: Date.now()
};

const productResponse: ApiResponse<ProductData> = {
data: { id: 101, name: "Laptop", price: 999.99 },
status: 'success',
timestamp: Date.now()
};

Ví dụ này cho thấy cách chúng ta có thể sử dụng các kiểu generic với các Loại ánh xạ để tạo ra các cấu trúc loại linh hoạt và tái sử dụng.

Tạo các loại ánh xạ tùy chỉnh

Bây giờ, hãy thử khả năng TypeScript của chúng ta và tạo ra một số Loại ánh xạ tùy chỉnh!

Loại tùy chỉnh 1: Nullable

Hãy tạo một loại làm cho tất cả các thuộc tính nullable:

type Nullable<T> = { [K in keyof T]: T[K] | null };

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

type NullablePerson = Nullable<Person>;

const maybePerson: NullablePerson = {
name: "Jane",
age: null  // Điều này bây giờ hợp lệ
};

Loại Nullable<T> cho phép bất kỳ thuộc tính nào có thể là loại ban đầu hoặc null.

Loại tùy chỉnh 2: Freezable

Hãy tạo một loại thêm phương thức freeze vào một đối tượng:

type Freezable<T> = T & { freeze(): Readonly<T> };

interface Config {
theme: string;
fontSize: number;
}

function makeFreezable<T>(obj: T): Freezable<T> {
return {
...obj,
freeze() {
return Object.freeze({ ...this }) as Readonly<T>;
}
};
}

const config = makeFreezable<Config>({
theme: "dark",
fontSize: 14
});

const frozenConfig = config.freeze();
// frozenConfig.theme = "light";  // Điều này sẽ gây ra lỗi

Loại này thêm phương thức freeze vào bất kỳ đối tượng nào, cho phép chúng ta tạo ra một phiên bản không thể thay đổi của nó.

Kết luận

Wow, chúng ta đã bao quát rất nhiều nội dung hôm nay! Từ các Loại ánh xạ内置 đến việc tạo ra các loại tùy chỉnh của riêng mình, bạn đã thấy TypeScript có thể mạnh mẽ và linh hoạt như thế nào. Các Loại ánh xạ giống như những pháp thuật trong bộ công cụ TypeScript của bạn - chúng cho phép bạn chuyển đổi và manipulates các loại theo những cách vô cùng hữu ích.

Nhớ rằng, chìa khóa để thành thạo các Loại ánh xạ là thực hành. Hãy thử tạo ra các loại của riêng bạn, thử nghiệm với các kết hợp khác nhau, và sớm bạn sẽ viết ra mã TypeScript không chỉ chức năng mà còn đẹp và an toàn về loại.

Tiếp tục lập mã, tiếp tục học hỏi, và quan trọng nhất, hãy vui vẻ với TypeScript!

Credits: Image by storyset