+1

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí