TYPESCRIPT 2.X CĂN BẢN
Hàm trong TypeScript Sử dụng hàm generics Sử dụng hàm generics trong TypeScript Sử dụng Array trong TypeScript Sử dụng Tuple trong TypeScript Sử dụng kiểu Array, Tuple, Enum trong TypeScript Interface trong Typescript Hybrid Types với Interface trong TypeScript Khác biệt khi sử dụng 'type' và 'interface' trong TypeScript Cách dùng Enum trong TypeScript Types và interface trong TypeScript Class trong TypeScript Sử dụng Abstract Classes trong TypeScript Sử dụng Interface trong Typescript (phần 2) Sử dụng Casting trong TypeScript Tìm hiểu về Generics trong TypeScript Tìm hiểu về utility types trong Typescript Utility types phổ biến trong Typescript Sử dụng default parameters trong TypeScript Modules trong TypeScript Rest Parameters trong TypeScript Sử dụng Function Overloadings trong TypeScript Sử dụng Type Assertions trong TypeScript Sử dụng Static Methods và Properties trong TypeScript. Sử dụng Access Modifiers trong TypeScript 10 mẹo và thủ thuật hay nhất trong TypeScript 5 Cách giúp bạn loại bỏ "any" trong TypeScript Mapped Types trong TypeScript Dependency Injection trong Typescript 5 ký hiệu khó hiểu cần biết trong TypeScript Cách sử dụng @ts-expect-error trong Typescript Declaration Merging trong TypeScript Tìm hiểu Branded Type trong TypeScript Namespaces trong Typescript Phân biệt Modules và Namespaces trong TypeScript Triple-Slash Directives trong TypeScript là gì? Phần 1: Biến trong TypeScript Phần 2: Biến trong TypeScript Property trong TypeScript Phân tích JSON trong TypeScript Bài tập TypeScript: Các dạng bài tập cơ bản Bài tập TypeScript: Lập trình Hướng đối tượng trong TypeScript Bài tập TypeScript: Kiểu Generic Types trong Typescript Bài tập TypeScript: Các kiểu dữ liệu mảng và tuple. Bài tập TypeScript: Classes và Inheritance trong Typescript Bài 01: TypeScript là gì? TypeScript và Javascript Bài 02: Cài đặt TypeScript Bài 03: TypeScript Basic Types Bài 04: Khai báo biến trong TypeScript Bài 05: Lệnh If Else và Switch Case trong TypeScript Bài 06: Vòng lặp trong TypeScript
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Sử dụng hàm generics trong TypeScript

Hàm generics là một tính năng quan trọng cho phép bạn làm việc với nhiều kiểu dữ liệu mà không cần phải viết lại mã nguồn nhiều lần. Điều này giúp tạo ra các hàm linh hoạt, tái sử dụng, và giảm thiểu sự trùng lặp trong mã nguồn.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Trong phần này, mình sẽ tìm hiểu cú pháp cơ bản của hàm generics, cách sử dụng chúng trong thực tế, và những ví dụ cụ thể về cách hàm generics có thể giải quyết các vấn đề thực tế. Hãy cùng tìm hiểu khía cạnh quan trọng này của TypeScript để nâng cao khả năng phát triển mã nguồn của bạn.

Generics là gì?

Hàm generics, thường được gọi là generics trong TypeScript và nhiều ngôn ngữ lập trình khác, là một tính năng cho phép bạn làm việc với nhiều kiểu dữ liệu khác nhau trong một cách tổng quát và tái sử dụng mã nguồn. Generics cho phép bạn tạo các hàm, lớp, và cấu trúc dữ liệu có thể hoạt động với nhiều kiểu dữ liệu mà bạn không cần biết trước.

Ở ngôn ngữ lập trình TypeScript, generics được thể hiện bằng sử dụng type parameter, tức là bạn định nghĩa một biến kiểu (type variable) trong hàm hoặc lớp và sau đó sử dụng biến kiểu này để làm việc với dữ liệu. Type parameter cho phép bạn áp dụng các kiểu dữ liệu khác nhau tùy thuộc vào cách hàm hoặc lớp được gọi.

Bài viết này được đăng tại [free tuts .net]

generic trong typescript png

Dưới đây là một ví dụ đơn giản về cách sử dụng hàm generics trong TypeScript để đảo ngược một mảng:

function daoNguocMang<T>(mang: T[]): T[] {
    return mang.reverse();
}

const mangSo: number[] = [1, 2, 3, 4, 5];
const mangChuoi: string[] = ["apple", "banana", "cherry"];

const mangSoDaoNguoc = daoNguocMang(mangSo);       // [5, 4, 3, 2, 1]
const mangChuoiDaoNguoc = daoNguocMang(mangChuoi); // ["cherry", "banana", "apple"]

Trong ví dụ này, T là type parameter, và mình sử dụng nó để định nghĩa kiểu dữ liệu của mảng đầu vào và đầu ra. Hàm daoNguocMang có thể hoạt động với mảng của bất kỳ kiểu dữ liệu nào (số, chuỗi, v.v.) mà bạn cung cấp cho nó.

Tính năng generics giúp bạn viết mã nguồn linh hoạt và tái sử dụng, giảm thiểu sự trùng lặp và giúp quản lý mã nguồn một cách hiệu quả khi bạn phải làm việc với nhiều kiểu dữ liệu khác nhau.

Cú pháp cơ bản của Generics

Trong phần này, mình sẽ tìm hiểu cú pháp cơ bản của hàm generics trong TypeScript và tìm hiểu cách khai báo hàm generic và sử dụng type parameter.

Khai báo hàm generic

Khi bạn muốn tạo một hàm generic trong TypeScript, bạn sử dụng type parameter. Type parameter là một biến kiểu (type variable) đặt tên bởi bạn, thường được ký hiệu bằng một chữ cái viết hoa (ví dụ: T, U, E, ...). Type parameter được đặt trong cặp dấu < và > sau tên hàm.

Ví dụ, đây là cú pháp khai báo một hàm generic đơn giản:

function tenHam<T>(thamSo: T): T {
    // Thực hiện các phép toán với thamSo
    return thamSo;
}

Trong ví dụ này, T là type parameter và hàm tenHam có thể hoạt động với bất kỳ kiểu dữ liệu nào.

Sử dụng type parameter

Sau khi bạn đã khai báo type parameter, bạn có thể sử dụng chúng trong hàm để thực hiện các phép toán hoặc kiểm tra kiểu dữ liệu của tham số và giá trị trả về.

Ví dụ sau đây sử dụng type parameter để kiểm tra kiểu dữ liệu của tham số và trả về giá trị:

function soLanLapLai<T>(mang: T[], phanTu: T): number {
    let soLan = 0;
    for (let i = 0; i < mang.length; i++) {
        if (mang[i] === phanTu) {
            soLan++;
        }
    }
    return soLan;
}

const mangSo: number[] = [1, 2, 2, 3, 4, 2, 5];
const soLanLapLaiSo2 = soLanLapLai(mangSo, 2); // Kết quả là 3

Trong ví dụ này, T là type parameter, và mình sử dụng nó để kiểm tra kiểu dữ liệu của mảng và phần tử phanTu.

Hàm generics cho phép bạn tạo các hàm tổng quát có thể làm việc với nhiều kiểu dữ liệu khác nhau và nó giúp bạn viết mã nguồn hiệu quả và tái sử dụng.

Sử dụng Generics trong thực tế

Trong phần này, mình sẽ thực hiện ba ví dụ cụ thể về việc sử dụng hàm generics để giải quyết các vấn đề thực tế.

Hàm Array Reverse sử dụng Generics

Mình sẽ tạo một hàm generics để đảo ngược thứ tự các phần tử trong một mảng.

function daoNguocMang<T>(mang: T[]): T[] {
    return mang.reverse();
}

const mangSo: number[] = [1, 2, 3, 4, 5];
const mangChuoi: string[] = ["apple", "banana", "cherry"];

const mangSoDaoNguoc = daoNguocMang(mangSo);       // Kết quả: [5, 4, 3, 2, 1]
const mangChuoiDaoNguoc = daoNguocMang(mangChuoi); // Kết quả: ["cherry", "banana", "apple"]

Hàm daoNguocMang có thể hoạt động với mảng của bất kỳ kiểu dữ liệu nào, từ số đến chuỗi.

Hàm Array Filter sử dụng Generics

Ta sẽ tạo một hàm generic để lọc các phần tử của mảng dựa trên một điều kiện nhất định.

function locMang<T>(mang: T[], dieuKien: (phanTu: T) => boolean): T[] {
    return mang.filter(dieuKien);
}

const mangSo: number[] = [1, 2, 3, 4, 5];
const soLe = locMang(mangSo, so => so % 2 === 1); // Kết quả: [1, 3, 5]

const mangChuoi: string[] = ["apple", "banana", "cherry"];
const chuoiCoKyTuE = locMang(mangChuoi, chuoi => chuoi.includes("e")); // Kết quả: ["apple", "cherry"]

Hàm locMang cho phép bạn lọc mảng bằng cách truyền một hàm điều kiện cho việc lọc.

Hàm Queue Generic

Mình sẽ xây dựng một cấu trúc dữ liệu hàng đợi (queue) sử dụng hàm generics.

class HangDoi<T> {
    private mangPhanTu: T[] = [];

    them(phanTu: T): void {
        this.mangPhanTu.push(phanTu);
    }

    lay(): T | undefined {
        if (this.khongRong()) {
            return this.mangPhanTu.shift();
        }
        return undefined;
    }

    kichThuoc(): number {
        return this.mangPhanTu.length;
    }

    khongRong(): boolean {
        return this.mangPhanTu.length > 0;
    }
}

const hangDoiSo = new HangDoi<number>();
hangDoiSo.them(1);
hangDoiSo.them(2);
const phanTuDau = hangDoiSo.lay(); // Kết quả: 1

const hangDoiChuoi = new HangDoi<string>();
hangDoiChuoi.them("apple");
hangDoiChuoi.them("banana");
const phanTuDauChuoi = hangDoiChuoi.lay(); // Kết quả: "apple"

Hàm HangDoi cho phép bạn tạo một hàng đợi với bất kỳ kiểu dữ liệu nào. Hàng đợi này hoạt động theo nguyên tắc "FIFO" (First-In-First-Out), tức là phần tử được thêm vào đầu tiên sẽ được lấy ra đầu tiên.

Ràng buộc (Constraints) trong Generics

Ràng buộc (constraints) trong hàm generics cho phép bạn áp dụng các ràng buộc hoặc giới hạn đối với type parameter. Điều này giúp kiểm tra và giới hạn kiểu dữ liệu mà type parameter có thể nhận. Dưới đây, mình sẽ tìm hiểu về hai loại ràng buộc quan trọng trong TypeScript.

Ràng buộc kiểu dữ liệu

Khi bạn muốn type parameter phải là một kiểu cụ thể hoặc phải thỏa mãn một điều kiện nào đó, bạn có thể sử dụng ràng buộc kiểu dữ liệu. Dưới đây là ví dụ:

function timPhanTuLonNhat<T>(mang: T[]): T {
    return Math.max(...mang); // Lỗi! TypeScript không biết làm thế nào với type parameter T
}

Trong trường hợp này, TypeScript không biết cách xử lý type parameter T vì nó có thể là bất kỳ kiểu dữ liệu nào. Để giải quyết vấn đề này, bạn có thể sử dụng ràng buộc kiểu dữ liệu để chỉ định rằng T phải là một kiểu dữ liệu có thể so sánh.

function timPhanTuLonNhat<T extends Comparable>(mang: T[]): T {
    return Math.max(...mang); // Bây giờ TypeScript biết rằng T phải là một kiểu có thể so sánh
}

// Ví dụ sử dụng:
const mangSo: number[] = [1, 2, 3, 4, 5];
const soLonNhat = timPhanTuLonNhat(mangSo); // Kết quả: 5

Ở ví dụ trên, T được giới hạn bởi extends Comparable, điều này đảm bảo rằng type parameter T phải là một kiểu dữ liệu có thể so sánh (ví dụ: số).

Ràng buộc cho số lần gọi Generic

Bạn cũng có thể giới hạn số lần mà hàm generic có thể được gọi trong hàm. Điều này có thể đảm bảo rằng bạn không sử dụng hàm generic quá nhiều lần trong một lúc.

function gopMang<T>(mang1: T[], mang2: T[]): T[] {
    // Làm việc với hai mảng và trả về một mảng kết quả
}

function gopHaiMang<T>(mang1: T[], mang2: T[]): T[] {
    return gopMang(gopMang(mang1, mang2), mang1); // Gọi hàm generic quá nhiều lần
}

// Sử dụng ràng buộc để giới hạn số lần gọi hàm generic
function gopHaiMang<T>(mang1: T[], mang2: T[]): T[] {
    if (mang1.length + mang2.length > 100) {
        throw new Error("Số lượng phần tử quá lớn.");
    }
    return gopMang(mang1, mang2);
}

Trong ví dụ trên, hàm gopHaiMang sử dụng ràng buộc để kiểm tra số lượng phần tử trong hai mảng trước khi gọi hàm generic gopMang. Điều này đảm bảo rằng hàm generic không bị gọi quá nhiều lần.

Ràng buộc trong hàm generics giúp kiểm tra và quản lý kiểu dữ liệu và số lần gọi hàm generic một cách an toàn trong TypeScript.

Ví dụ cụ thể của Generics

Trong phần này, mình sẽ xem xét một số ví dụ cụ thể về cách sử dụng hàm generics để giải quyết các vấn đề thực tế và phân tích cách chúng đóng góp vào tính linh hoạt và tái sử dụng của mã nguồn.

Ví dụ: Generic cho tính toán số lớn

Một ứng dụng khác là sử dụng generics để tạo một hàm cho tính toán số lớn (big numbers) dựa trên thư viện số lớn nào đó.

function congHaiSoLon<T>(a: T, b: T): T {
    return a.add(b);
}
const so1 = new BigNumber("12345678901234567890");
const so2 = new BigNumber("98765432109876543210");
const ketQua = congHaiSoLon(so1, so2);

Trong ví dụ này, mình sử dụng generics để tạo hàm congHaiSoLon, cho phép mình tính toán với số lớn sử dụng thư viện số lớn nào đó mà không cần viết lại hàm cho từng kiểu dữ liệu số lớn.

Ví dụ: Generic cho quản lý thư viện dữ liệu

Generics cũng có thể được sử dụng để quản lý thư viện dữ liệu. Mình có thể tạo một cơ sở dữ liệu generic cho nhiều loại đối tượng.

class CoSoDuLieu<T> {
    private duLieu: T[] = [];

    themMoi(duLieuMoi: T): void {
        this.duLieu.push(duLieuMoi);
    }

    layTatCa(): T[] {
        return this.duLieu;
    }
}

const coSoDuLieuNguoiDung = new CoSoDuLieu<NguoiDung>();
coSoDuLieuNguoiDung.themMoi({ id: 1, ten: "Alice" });

const coSoDuLieuSanPham = new CoSoDuLieu<SanPham>();
coSoDuLieuSanPham.themMoi({ maSanPham: "12345", tenSanPham: "Bút bi" });

Trong ví dụ này, mình sử dụng generics để tạo một cơ sở dữ liệu có thể lưu trữ nhiều loại đối tượng khác nhau mà không cần viết lại cơ sở dữ liệu cho từng loại đối tượng.

Như bạn có thể thấy, hàm generics cung cấp tính linh hoạt và tái sử dụng mạnh mẽ trong việc giải quyết các vấn đề thực tế trong phát triển phần mềm. Chúng giúp bạn viết mã nguồn sạch sẽ và hiệu quả mà không cần lặp lại công việc.

Kết bài

Trong bài viết này, mình đã tìm hiểu một loạt các khía cạnh liên quan đến hàm generics trong TypeScript.Ta đã bắt đầu bằng việc hiểu về cú pháp cơ bản của hàm generics, cách khai báo và sử dụng type parameter.

Tiếp theo, mình đã tìm hiểu về các khía cạnh quan trọng bao gồm hàm generics, khả năng sử dụng type parameter, và cách chúng đóng góp vào tính linh hoạt và tái sử dụng của mã nguồn.

Cuối cùng, mình đã xem xét các ví dụ cụ thể về cách sử dụng hàm generics trong các tình huống thực tế, bao gồm xử lý dữ liệu API, tính toán số lớn, và quản lý thư viện dữ liệu. Những ví dụ này đã minh họa sức mạnh và ứng dụng thực tế của hàm generics trong TypeScript.

Hàm generics là một công cụ mạnh mẽ để viết mã nguồn linh hoạt, tái sử dụng và hiệu quả trong TypeScript. Việc hiểu và sử dụng chúng sẽ cải thiện khả năng phát triển và quản lý mã nguồn của bạn.

Cùng chuyên mục:

Bài tập TypeScript: Classes và Inheritance trong Typescript

Bài tập TypeScript: Classes và Inheritance trong Typescript

Bài tập TypeScript: Các kiểu dữ liệu mảng và tuple.

Bài tập TypeScript: Các kiểu dữ liệu mảng và tuple.

Bài tập TypeScript: Kiểu Generic Types trong Typescript

Bài tập TypeScript: Kiểu Generic Types trong Typescript

Bài tập TypeScript: Lập trình Hướng đối tượng trong TypeScript

Bài tập TypeScript: Lập trình Hướng đối tượng trong TypeScript

Bài tập TypeScript: Các dạng bài tập cơ bản

Bài tập TypeScript: Các dạng bài tập cơ bản

Phân tích JSON trong TypeScript

Phân tích JSON trong TypeScript

Property trong TypeScript

Property trong TypeScript

Phần 2: Biến trong TypeScript

Phần 2: Biến trong TypeScript

Phần 1: Biến trong TypeScript

Phần 1: Biến trong TypeScript

Triple-Slash Directives trong TypeScript là gì?

Triple-Slash Directives trong TypeScript là gì?

Phân biệt Modules và Namespaces trong TypeScript

Phân biệt Modules và Namespaces trong TypeScript

Tìm hiểu Branded Type trong TypeScript

Tìm hiểu Branded Type trong TypeScript

Namespaces trong Typescript

Namespaces trong Typescript

Declaration Merging trong TypeScript

Declaration Merging trong TypeScript

Cách sử dụng @ts-expect-error trong typescript

Cách sử dụng @ts-expect-error trong typescript

5 ký hiệu khó hiểu cần biết trong TypeScript

5 ký hiệu khó hiểu cần biết trong TypeScript

Dependency Injection trong Typescript

Dependency Injection trong Typescript

Mapped Types trong TypeScript

Mapped Types trong TypeScript

5 Cách giúp bạn loại bỏ

5 Cách giúp bạn loại bỏ "any" trong TypeScript

10 mẹo và thủ thuật hay nhất trong TypeScript

10 mẹo và thủ thuật hay nhất trong TypeScript

Top