Bài 3. Hàm có kiểu

Hàm là nơi kiểu phát huy tác dụng nhất. Bài này học cách khai báo tham số, giá trị trả về, tham số tùy chọn, overload và kiểu hàm.

40 phútCơ bản
Bạn sẽ học được
  • Gán kiểu tham số và giá trị trả về
  • Dùng tham số tùy chọn, mặc định, rest
  • Khai báo kiểu hàm và callback
  • Viết overload và type guard
Thuật ngữ cần biết
Chữ ký hàm (signature)
Mô tả kiểu tham số và kiểu trả về.
Overload
Nhiều chữ ký cho cùng một hàm.
Type guard
Hàm trả về x is T để thu hẹp kiểu.

Khái niệm và lý thuyết

Mỗi hàm có một chữ ký: danh sách tham số kèm kiểu và kiểu trả về. TS kiểm tra lời gọi hàm có đúng số lượng, đúng kiểu đối số không. Kiểu trả về thường được suy luận, nhưng ghi tường minh giúp phát hiện sớm khi thân hàm trả sai.

Hàm trong JS là giá trị nên có kiểu hàm, ví dụ (a: number, b: number) => number. Kiểu tham số hàm là phản biến (contravariant): một hàm nhận kiểu rộng hơn có thể dùng ở chỗ cần hàm nhận kiểu hẹp hơn.

Tham số và giá trị trả về

function tong(a: number, b: number): number {
  return a + b;
}
const nhan = (a: number, b = 2): number => a * b;  // mặc định
function chao(ten: string, danhXung?: string) {     // tùy chọn
  return `Xin chào ${danhXung ?? ""} ${ten}`.trim();
}
function tongTatCa(...so: number[]): number {        // rest
  return so.reduce((s, x) => s + x, 0);
}

Kiểu hàm và callback

type PhepToan = (a: number, b: number) => number;
const tru: PhepToan = (a, b) => a - b; // a, b tự có kiểu number

function apDung(ds: number[], f: (x: number) => number): number[] {
  return ds.map(f);
}
apDung([1, 2, 3], x => x * x); // [1, 4, 9]

Overload

function dinhDang(x: number): string;
function dinhDang(x: Date): string;
function dinhDang(x: number | Date): string {
  return typeof x === "number" ? x.toLocaleString("vi-VN")
                               : x.toLocaleDateString("vi-VN");
}

Chỉ các chữ ký phía trên được nhìn thấy từ bên ngoài; chữ ký cuối là phần cài đặt. Nếu union đủ diễn đạt, ưu tiên union thay vì overload.

Type guard và assertion function

interface Meo { keu(): void }
interface Cho { sua(): void }
function laMeo(x: Meo | Cho): x is Meo {
  return "keu" in x;
}
function khangDinh(dk: unknown, msg: string): asserts dk {
  if (!dk) throw new Error(msg);
}
Mẹo hay

Với callback xử lý sự kiện, khai báo kiểu trả về void: TS cho phép truyền hàm trả về giá trị bất kỳ, nhưng bên gọi sẽ bỏ qua giá trị đó.

Lưu ý

Tham số tùy chọn phải đứng sau tham số bắt buộc. Truy cập tham số tùy chọn khi chưa kiểm tra sẽ báo lỗi “possibly undefined” dưới chế độ strict.

Thực hành
  1. Viết hàm tinhDiemTB(ds: number[]): number trả 0 nếu mảng rỗng.
  2. Khai báo kiểu BoLoc<T> là hàm nhận T trả boolean, dùng với filter.
  3. Viết overload cho hàm layGiaTri nhận id số hoặc mã chuỗi.
  4. Viết type guard laChuoi(x: unknown): x is string.
Tóm tắt bài học
  • Ghi kiểu tham số luôn; kiểu trả về nên ghi cho hàm công khai.
  • Kiểu hàm mô tả callback.
  • Overload khi các dạng gọi khác biệt rõ; còn lại dùng union.
  • Type guard và asserts giúp thu hẹp kiểu an toàn.