TypeScript là một ngôn ngữ lập trình mạnh mẽ dựa trên JavaScript, cung cấp các tính năng mở rộng như kiểu tĩnh, giúp phát hiện lỗi ngay khi viết mã, và hỗ trợ lập trình hướng đối tượng. Trong bài viết này, chúng ta sẽ khám phá các thành phần quan trọng của TypeScript, từ các kiểu dữ liệu cơ bản cho đến các tính năng tiên tiến như generic, interface, và module. Qua đó, bạn sẽ nắm bắt được những yếu tố giúp TypeScript trở thành một ngôn ngữ hiệu quả và an toàn hơn so với JavaScript.

Kiểu dữ liệu trong TypeScript

Kiểu dữ liệu đóng vai trò cốt lõi trong TypeScript, giúp lập trình viên chỉ định và kiểm tra loại dữ liệu của biến và tham số, đảm bảo mã nguồn được viết một cách an toàn và rõ ràng.

Kiểu dữ liệu nguyên thủy (Primitive Types)

TypeScript cung cấp các kiểu dữ liệu nguyên thủy tương tự như JavaScript, bao gồm:

  • number: Dùng để lưu trữ số, bao gồm cả số nguyên và số thực.
  • string: Lưu trữ chuỗi ký tự.
  • boolean: Chứa giá trị đúng hoặc sai.
  • nullundefined: Biểu thị giá trị không xác định hoặc không tồn tại.
  • symbolbigint: Được sử dụng cho các giá trị đặc biệt như biểu tượng duy nhất và số nguyên lớn.
let age: number = 30;
let name: string = "Alice";
let isActive: boolean = true;
let uniqueSymbol: symbol = Symbol("unique");
let bigNumber: bigint = 9007199254740991n;

Kiểu dữ liệu phức tạp

Bên cạnh các kiểu nguyên thủy, TypeScript cũng hỗ trợ các kiểu dữ liệu phức tạp hơn để tổ chức và quản lý dữ liệu hiệu quả.

  • Array: Lưu trữ danh sách các giá trị cùng loại.
let numbers: number[] = [1, 2, 3];
let names: string[] = ["Alice", "Bob"];
  • Tuple: Một mảng có số lượng phần tử cố định, với mỗi phần tử có thể có kiểu khác nhau.
let person: [string, number] = ["Alice", 25];
  • Enum: Được sử dụng để định nghĩa một tập hợp các giá trị hằng số có tên.
enum Color {
  Red,
  Green,
  Blue
}

let favoriteColor: Color = Color.Green;

Interface trong TypeScript

Interface trong TypeScript là một cách để định nghĩa cấu trúc của đối tượng. Nó giúp kiểm tra rằng một đối tượng có đầy đủ các thuộc tính và kiểu dữ liệu phù hợp.

interface Person {
  name: string;
  age: number;
}

let alice: Person = {
  name: "Alice",
  age: 30
};

Interface có thể mở rộng lẫn nhau, cho phép bạn xây dựng các đối tượng phức tạp hơn:

interface Employee extends Person {
  employeeId: number;
}

let bob: Employee = {
  name: "Bob",
  age: 40,
  employeeId: 1234
};

Class trong TypeScript

Class trong TypeScript là một phần quan trọng cho phép lập trình hướng đối tượng, với tính năng kế thừa, tính đóng gói, và tính đa hình. Bạn có thể tạo các lớp để định nghĩa đối tượng với các thuộc tính và phương thức cụ thể.

class Animal {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
  
  speak(): void {
    console.log(`${this.name} makes a noise.`);
  }
}

let dog = new Animal("Dog");
dog.speak();

Tính kế thừa (Inheritance)

TypeScript hỗ trợ kế thừa giữa các lớp, cho phép một lớp con mở rộng lớp cha và thêm hoặc ghi đè các phương thức và thuộc tính.

class Dog extends Animal {
  speak(): void {
    console.log(`${this.name} barks.`);
  }
}

let dog = new Dog("Buddy");
dog.speak(); // Buddy barks.

Generic trong TypeScript

Generic trong TypeScript cho phép bạn tạo ra các hàm, lớp, hoặc interface mà không cần chỉ định trước kiểu dữ liệu, giúp chúng linh hoạt hơn và có thể sử dụng với nhiều loại dữ liệu khác nhau.

function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<number>(42);
let output2 = identity<string>("Hello");

Generics giúp đảm bảo rằng kiểu dữ liệu được giữ nguyên trong suốt quá trình thực thi, cung cấp tính linh hoạt mà vẫn đảm bảo an toàn kiểu dữ liệu.

Module trong TypeScript

TypeScript cho phép bạn chia mã nguồn thành các module để dễ dàng quản lý và tổ chức mã. Một module có thể chứa các lớp, hàm, hoặc biến và có thể được xuất (export) để sử dụng ở nơi khác.

// module.ts
export class User {
  constructor(public name: string) {}
}

// main.ts
import { User } from './module';
let user = new User("Alice");

Type Assertion trong TypeScript

Type assertion trong TypeScript cho phép lập trình viên chỉ định rõ kiểu của một biến mà không cần phải chuyển đổi nó. Điều này hữu ích khi bạn đã biết kiểu dữ liệu của biến nhưng TypeScript chưa tự động nhận diện được.

let someValue: any = "Hello, TypeScript!";
let strLength: number = (someValue as string).length;

Type assertion giúp lập trình viên kiểm soát chính xác kiểu dữ liệu mà họ biết hoặc mong đợi, tránh lỗi khi làm việc với kiểu dữ liệu any.

Hệ thống kiểu nâng cao

TypeScript cung cấp các tính năng kiểu nâng cao như union types, intersection types và conditional types, giúp định nghĩa các kiểu phức tạp hơn.

Union Types

Union types cho phép một biến có thể là một trong nhiều kiểu khác nhau.

let id: number | string;
id = 123; // valid
id = "abc"; // valid

Intersection Types

Intersection types kết hợp nhiều kiểu lại thành một kiểu duy nhất, yêu cầu biến phải thỏa mãn tất cả các kiểu.

interface A {
  x: number;
}

interface B {
  y: number;
}

type C = A & B;

let point: C = { x: 10, y: 20 };

Conditional Types

Conditional types là một cách để định nghĩa kiểu dữ liệu dựa trên điều kiện.

type IsString<T> = T extends string ? "yes" : "no";

type Test1 = IsString<string>;  // "yes"
type Test2 = IsString<number>;  // "no"

TypeScript cung cấp một loạt các công cụ mạnh mẽ để kiểm soát và mở rộng mã của bạn, từ hệ thống kiểu nâng cao đến các tính năng lập trình hướng đối tượng và generic. Khi sử dụng các thành phần này, bạn có thể viết mã nguồn vừa hiệu quả vừa dễ bảo trì. Việc hiểu rõ và áp dụng chúng giúp nâng cao chất lượng mã và tạo ra các ứng dụng an toàn và ổn định hơn.