openplanning

Hướng dẫn và ví dụ hàm trong TypeScript

  1. Hàm là gì?
  2. Hàm thông thường
  3. Closure (Bao đóng)
  4. Tham số tuỳ chọn
  5. Các tham số với kiểu dữ liệu liên hợp
  6. Function Overloading

1. Hàm là gì?

Trong ngôn ngữ lập trình TypeScript, hàm (function) là một khối mã (block of code) được đặt tên. Nó có thể bao gồm các tham số và trả về một giá trị hoặc không trả về gì cả, và chỉ chạy khi được gọi. Các hàm chia nhiệm vụ lớn thành các phần nhỏ và thực hiện hoạt động cụ thể của chương trình đó. Quá trình này làm tăng khả năng tái sử dụng mã và nâng cao cách tiếp cận mô-đun của chương trình.
Cú pháp khai báo một hàm thông thường:
function function_name(arg1: type1, arg2: type2, argN: typeN) : return_type {
   // Statements
}
Chú ý: Cũng giống như JavaScript, TypeScript không cho phép 2 hàm cùng tên, kể cả khi chúng có các tham số khác nhau.

2. Hàm thông thường

Cú pháp khai báo một hàm thông thường:
function function_name(arg1: type1, arg2: type2, argN: typeN) : return_type {
   // Statements
}
Sử dụng từ khoá void (Hoặc rỗng) như một kiểu trả về nếu hàm không trả về gì cả.
function function_name(arg1: type1, arg2: type2, argN: typeN) : void {
   // Statements
}
// Same as:
function function_name(arg1: type1, arg2: type2, argN: typeN) {
   // Statements
}
Ví dụ: Hàm minus với hai tham số kiểu number, và trả về một giá trị kiểu number.
function_ex1.ts
function minus(a: number, b: number) : number {
    return a + b;
}  
var result = minus(10, 20); // -10
console.log(`result = ${result}`);
Output:
result = -10
Ví dụ: Hàm greeting chấp nhận một tham số kiểu string và không trả về gì cả.
function_ex2.ts
function greeting(name: string) {
    var s = `Hello $name`;
    console.log(s);
}
// Call the function:
greeting('Tom'); // Hello Tom

3. Closure (Bao đóng)

Trong ngôn ngữ lập trình TypeScript, Closure (bao đóng) là một hàm đặc biệt.
  • Giống như hàm, Closure là một khối lệnh với các tham số và có thể trả về một giá trị hoặc không trả về gì cả.
  • Khác với hàm, Closure không có tên, tuy nhiên bạn có thể định danh nó thông qua một biến.
Xem bài viết chi tiết về Closure:

4. Tham số tuỳ chọn

Như đã đề cập ở trên, TypeScript không cho phép các hàm cùng tên, nhưng một hàm có thể bao gồm các tham số tuỳ chọn, chúng phải là các tham số đặt tại cuối cùng trong danh sách các tham số.
Cú pháp:
Syntax
function function_name(argM: typeM, argN: typeN, argP?:typeP, argQ?: typeQ): return_type {  
     // Statement(s)  
}
Hoặc cú pháp - Tham số tuỳ chọn với giá trị mặc định:
function function_name(argM: typeM, argN: typeN,
                  argP?:typeP = defaultValueP, argQ?: typeQ = defaultValueQ): return_type {  
     // Statement(s)  
}
Ví dụ:
function_optional_args_ex1.ts
function concat(s1: string, s2: string, s3?: string): string {
    if (s3) {
        return s1 + s2 + s3;
    }
    return s1 + s2;
}
function sum(v1: number, v2: number, v3?: number, v4?: number): number {
    return v1 + v2 + (v3 ?? 0) + (v4 ?? 0);
}
function function_optional_args_ex1_test() {
    var result1 = concat('One', 'Two');
    console.log(`result1: ${result1}`);
    var result2 = concat('One', 'Two', 'Three');
    console.log(`result2: ${result2}`);

    var value1 = sum(1, 2, 3, 4);
    console.log(`value1: ${value1}`);
    var value2 = sum(1, 2, 3);
    console.log(`value2: ${value2}`);
    var value3 = sum(1, 2);
    console.log(`value3: ${value3}`);
}
function_optional_args_ex1_test(); // Call the function.
Output:
result1: OneTwo
result2: OneTwoThree
value1: 10
value2: 6
value3: 3

5. Các tham số với kiểu dữ liệu liên hợp

Tham số trong một hàm cũng có thể được khai báo với kiểu dữ liệu liên hợp (union data type).
Cú pháp:
function function_name (
             arg1 : data_type1, arg2 : data_type2,
             arg3 : data_type31 | data_type32 | data_type3N, // Union Data Type
             arg4 : data_type4) : return_type {
    //  Statament(s)
}
Ví dụ:
function_union_type_args_ex1.ts
interface IStudent {
    studentId: number,
    studentName: string
}
interface IWorker {
    workerId: number,
    workerName: string
}
function getName(person: IStudent | IWorker): string { // Union Type Arg
    var p = person as IStudent;
    if (p.studentName) {
        return p.studentName;
    }
    return (person as IWorker).workerName;
}
function function_union_type_args_ex1_test() {
    var student = { studentId: 1, studentName: "Tom" };
    var worker = { workerId: 2, workerName: "Jerry" };

    var name1 = getName(student);
    var name2 = getName(worker);
    console.log(`name1: ${name1}`);
    console.log(`name2: ${name2}`);
}
function_union_type_args_ex1_test(); // Call the function.
Output:
name1: Tom
name2: Jerry

6. Function Overloading

Như đã đề cập ở trên, TypeScript không cho phép các hàm cùng tên kể cả trường hợp chúng có các tham số khác nhau. Function Overloading (nạp chồng hàm) là một kỹ thuật "lách luật" trên. Nghĩa là bạn vẫn chỉ có một hàm với một tên cụ thể nhưng có thể sử dụng nó với các tham số khác nhau.
Ý tưởng chính để nạp chồng (overload) cho hàm là tạo một hàm chung để kiểm tra loại tham số nào đã được truyền vào khi hàm được gọi, và sau đó thực hiện một số logic cho trường hợp thích hợp. Hữu ích là thêm các định nghĩa cho hàm để giúp các lập trình viên khác biết cách sử dụng nó một cách thích hợp.
Cú pháp:
// Definition 1
function function_name(arg_11 : type_11, arg_1N : type_1N): return_type;
// Definition 2
function function_name(arg_21 : type_21, arg_22 : type_22, arg_2M : type_2M) : return_type;
function function_name(... args : any[]) : return_type {
   // Function body.
}
Về cơ bản, có rất nhiều cách để định nghĩa một hàm nạp chồng. Cú pháp trên được khuyến khích sử dụng, nó giúp bạn tránh được thông báo lỗi giống như dưới đây từ trình biên dịch:
This overload signature is not compatible with its implementation signature.
Ví dụ:
function_overload_ex1.ts
interface IDimension3D {
    width: number,
    height: number,
    depth: number
}
function getVolume(dimension: IDimension3D): number; // Definition 1
function getVolume(width: number): number; // Definition 2
function getVolume(width: number, height: number, depth: number): number; // Definition 3
function getVolume(...args: any[]): number {
    if (args.length == 1) {
        if (typeof args[0] == 'number') { // Use Definition 1
            return args[0] * args[0] * args[0];
        } else {  // Use Definition 2
            var dim = args[0] as IDimension3D;
            return dim.width * dim.height * dim.depth;
        }
    } else if (args.length == 3) { // Use definition 3
        return args[0] * args[1] * args[2];
    } else {
        throw Error('Argument invalid!');
    }
}
function function_overload_ex1_test() {
    var volume1 = getVolume(10); // 1000
    var volume2 = getVolume({ width: 10, height: 20, depth: 30 }); // 6000
    var volume3 = getVolume(5, 10, 15); // 750

    console.log(`volume1 = ${volume1}`);
    console.log(`volume2 = ${volume2}`);
    console.log(`volume3 = ${volume3}`);
}
function_overload_ex1_test(); // Call the function.
Output:
volume1 = 1000
volume2 = 6000
volume3 = 750