20 bí kíp TypeScript mà mọi lập trình viên đều nên biết
TypeScript là công cụ mạnh mẽ cho phát triển JavaScript hiện đại, mang lại tính an toàn kiểu dữ liệu và các tính năng nâng cao. Bài viết này sẽ khám phá 20 bí kíp TypeScript mà mọi lập trình viên nên biết, kèm theo ví dụ và cách tiếp cận thực tế.
1. Kiểu dữ liệu không Null
TypeScript cung cấp tiện ích NonNullable để loại bỏ null và undefined khỏi một kiểu. Điều này giúp bạn tránh các giá trị null không mong muốn.
type User = { name: string; age?: number | null };
const user: NonNullable<User["age"]> = 30; // ✅ No null or undefined allowed
2. Kiểu dữ liệu tùy chọn
Tiện ích Partial<T> làm cho tất cả các thuộc tính trong một kiểu trở nên tùy chọn, rất hữu ích khi bạn chỉ cập nhật một tập hợp con của các trường đối tượng.
interface User {
name: string;
age: number;
email: string;
}
const updateUser = (user: Partial<User>) => {
// You can pass only the fields you want to update
return { ...user, updatedAt: new Date() };
};
updateUser({ name: 'John' }); // No need to provide the entire object
3. Kiểu dữ liệu bất biến
Khi bạn cần tính bất biến trong TypeScript, Readonly<T> làm cho tất cả các thuộc tính của một kiểu không thể thay đổi, ngăn chặn việc gán lại.
const config: Readonly<{ apiUrl: string; retries: number }> = {
apiUrl: 'https://api.example.com',
retries: 5
};
config.apiUrl = 'https://newapi.com'; // ❌ Error: Cannot assign to 'apiUrl' because it is a read-only property
4. Kiểu dữ liệu được ánh xạ
Kiểu dữ liệu được ánh xạ cho phép bạn tạo các kiểu mới bằng cách biến đổi các kiểu hiện có. Điều này rất tiện dụng để tạo các biến thể của kiểu đối tượng.
type Status = 'loading' | 'success' | 'error';
type ApiResponse<T> = {
[K in Status]: T;
};
const response: ApiResponse<string> = {
loading: 'Fetching...',
success: 'Data loaded',
error: 'Something went wrong'
};
5. Kiểu Tuple với các phần tử tùy chọn
Bạn có biết TypeScript cho phép các phần tử tùy chọn trong tuple không? Điều này rất tuyệt vời khi xử lý các đối số hàm biến đổi.
type UserTuple = [string, number?, boolean?];
const user1: UserTuple = ['Alice']; // ✅ Just the name
const user2: UserTuple = ['Bob', 30]; // ✅ Name and age
const user3: UserTuple = ['Charlie', 25, true]; // ✅ Full tuple
6. Kiểu dữ liệu kiểm tra toàn diện
Đảm bảo bạn đang xử lý tất cả các trường hợp có thể xảy ra với các kiểu hợp nhất và kiểm tra toàn diện trong câu lệnh switch.
type Status = 'open' | 'closed' | 'pending';
function handleStatus(status: Status) {
switch (status) {
case 'open':
return 'Opened';
case 'closed':
return 'Closed';
case 'pending':
return 'Pending';
default:
const exhaustiveCheck: never = status; // ❌ Error if a new status type is added but not handled
return exhaustiveCheck;
}
}
7. Kiểu dữ liệu loại trừ khóa
Đôi khi bạn cần tạo một kiểu đối tượng loại trừ một số khóa nhất định. Omit chính là người bạn của bạn ở đây!
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, 'description'>;
const todo: TodoPreview = {
title: 'Learn TypeScript',
completed: false
};
8. Kiểu dữ liệu thu hẹp với in và instanceof
Sử dụng in và instanceof để thu hẹp các kiểu trong thời gian chạy.
function processInput(input: string | number | { title: string }) {
if (typeof input === 'string') {
return input.toUpperCase(); // Narrowed to string
} else if (typeof input === 'number') {
return input * 2; // Narrowed to number
} else if ('title' in input) {
return input.title; // Narrowed to object with title property
}
}
9. Kiểu dữ liệu có điều kiện
Các kiểu có điều kiện mang lại cho bạn sự linh hoạt đáng kinh ngạc để chuyển đổi các kiểu dựa trên các điều kiện.
type IsString<T> = T extends string ? true : false;
type CheckString = IsString<'Hello'>; // true
type CheckNumber = IsString<42>; // false
10. Kiểu dữ liệu bất biến
Sử dụng as const rất tuyệt vời để đóng băng các giá trị và đảm bảo rằng TypeScript coi chúng là các kiểu literal, không phải các giá trị có thể thay đổi.
const COLORS = ['red', 'green', 'blue'] as const;
type Color = typeof COLORS[number]; // 'red' | 'green' | 'blue'
11. Kiểu trích xuất và loại trừ
Sử dụng Extract và Exclude để lọc ra hoặc chọn các kiểu cụ thể từ một hợp nhất.
type T = 'a' | 'b' | 'c';
type OnlyAOrB = Extract<T, 'a' | 'b'>; // 'a' | 'b'
type ExcludeC = Exclude<T, 'c'>; // 'a' | 'b'
12. Kiểu bảo vệ cho xác thực tùy chỉnh
Tạo bảo vệ kiểu của riêng bạn để tinh chỉnh các kiểu một cách linh hoạt trong thời gian chạy.
function isString(input: any): input is string {
return typeof input === 'string';
}
const value: any = 'Hello';
if (isString(value)) {
console.log(value.toUpperCase()); // Safe: value is a string here
}
13. Kiểu dữ liệu động
Khi bạn cần một kiểu cho một đối tượng có các khóa động, Record<K, V> là lựa chọn hoàn hảo.
type Role = 'admin' | 'user' | 'guest';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};
14. Thuộc tính Dynamic class với chữ ký chỉ mục
Chỉ số này cho phép bạn tạo các đối tượng hoặc các lớp có các thuộc tính được đặt tên động.
class DynamicObject {
[key: string]: any;
}
const obj = new DynamicObject();
obj.name = 'Alice';
obj.age = 30;
15. Kiểu never cho các trạng thái không thể xảy ra
Kiểu never đại diện cho các giá trị không bao giờ xảy ra. Nó thường được sử dụng trong các kiểm tra toàn diện.
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}
16. Sử dụng chuỗi tùy chọn để truy cập an toàn
Sử dụng chuỗi tùy chọn (?.) để truy cập an toàn các thuộc tính lồng nhau mà không cần lo lắng về lỗi undefined.
const user = { profile: { name: 'John' } };
const userName = user?.profile?.name; // 'John'
const age = user?.profile?.age ?? 'Not provided'; // Fallback to default
17. Giá trị mặc định với Nullish Coalescing (??)
Sử dụng toán tử kết hợp null để cung cấp giá trị dự phòng chỉ khi giá trị ban đầu là null hoặc undefined.
const input: string | null = null;
const defaultValue = input ?? 'Default'; // 'Default'
18. Kiểu trả về
Tiện ích ReturnType<T> trích xuất kiểu trả về của một hàm, điều này có thể hữu ích khi bạn đang xử lý các kiểu phức tạp.
function getUser() {
return { name: 'John', age: 30 };
}
type UserReturn = ReturnType<typeof getUser>; // { name: string; age: number; }
19. Kiểu tham số trong hàm
Kiểu tham số chung làm cho các hàm của bạn linh hoạt và có thể tái sử dụng trên các kiểu khác nhau.
function identity<T>(value: T): T {
return value;
}
identity<string>('Hello'); // 'Hello'
identity<number>(42); // 42
20. Kiểu giao nhau
Kiểu giao nhau cho phép bạn kết hợp nhiều kiểu thành một.
type Admin = { privileges: string[] };
type User = { name: string };
type AdminUser = Admin & User;
const adminUser: AdminUser = {
privileges: ['admin', 'editor'],
name: 'Alice'
};
Những bí kíp này sẽ giúp bạn nâng cao kỹ năng TypeScript của mình lên một tầm cao mới! Hãy tiếp tục thử nghiệm và tích hợp các mẫu này vào các dự án của bạn để có mã sạch hơn và hiệu quả hơn.
All rights reserved