# TypeScript - null và undefined

Xin chào các bạn đang học lập trình! Hôm nay, chúng ta sẽ cùng nhau khám phá một chủ đề thú vị mà thường làm rối loạn những người mới bắt đầu: sự khác biệt giữa `null` và `undefined` trong TypeScript. Đừng lo lắng nếu bạn cảm thấy hơi choáng ngợp - tôi nhớ khi lần đầu tiên tôi gặp những khái niệm này, tôi cũng từng phải gãi đầu không hiểu! Nhưng đến cuối bài học này, bạn sẽ thành thạo trong việc phân biệt giữa hai giá trị đặc biệt này. Hãy bắt đầu nhé!

## `null` là gì?

Trong TypeScript (và JavaScript), `null` là một giá trị đặc biệt biểu thị sự vắng mặt có chủ ý của bất kỳ giá trị đối tượng nào. Nó giống như nói, "Ở đây có điều gì đó, nhưng hiện tại, không có gì cả."

Hãy cùng xem một số ví dụ để hiểu rõ hơn:

```typescript
let myPet: string | null = null;
console.log(myPet); // Output: null

// Sau này trong mã...
myPet = "Fluffy";
console.log(myPet); // Output: Fluffy

Trong ví dụ này, chúng ta khai báo một biến myPet có thể là một chuỗi hoặc null. Ban đầu, chúng ta đặt nó thành null, cho thấy rằng chúng ta chưa có宠物. Sau đó, khi chúng ta có một宠物, chúng ta gán tên "Fluffy" cho myPet.

TypeScript - null vs. undefined

Đây là một ví dụ khác:

function findUser(id: number): { name: string } | null {
    // Hãy tưởng tượng chúng ta đang tìm kiếm trong cơ sở dữ liệu
    if (id === 1) {
        return { name: "Alice" };
    } else {
        return null;
    }
}

let user = findUser(1);
console.log(user); // Output: { name: "Alice" }

user = findUser(2);
console.log(user); // Output: null

Trong trường hợp này, hàm findUser của chúng ta trả về hoặc một đối tượng người dùng hoặc null nếu không tìm thấy người dùng. Đây là một mẫu mã phổ biến trong lập trình - sử dụng null để chỉ ra rằng một tìm kiếm hoặc thao tác không mang lại kết quả.

undefined là gì?

Bây giờ, hãy nói về undefined. Giá trị đặc biệt này biểu thị một biến đã được khai báo nhưng chưa được gán giá trị. Nó giống như một hộp trống - nó tồn tại, nhưng chưa có gì trong đó.

Dưới đây là một số ví dụ minh họa undefined:

let myName: string;
console.log(myName); // Output: undefined

// Sau này trong mã...
myName = "John";
console.log(myName); // Output: John

function greet(name?: string) {
    console.log(name);
}

greet(); // Output: undefined
greet("Alice"); // Output: Alice

Trong phần đầu tiên, chúng ta khai báo myName nhưng không gán giá trị. TypeScript tự động gán cho nó giá trị undefined. Sau đó, chúng ta gán một giá trị, và nó không còn là undefined nữa.

Trong hàm greet, chúng ta sử dụng một tham số tùy chọn. Nếu chúng ta gọi hàm mà không cung cấp tham số, tham số name sẽ là undefined.

Đây là một tình huống khác mà bạn có thể gặp undefined:

let person = {
    name: "Bob",
    age: 30
};

console.log(person.name); // Output: Bob
console.log(person.job); // Output: undefined

Trong trường hợp này, person.jobundefined vì chúng ta chưa định nghĩa thuộc tính job cho đối tượng person.

So sánh nullundefined: Những khác biệt chính

Bây giờ chúng ta đã khám phá nullundefined riêng lẻ, hãy so sánh chúng bên nhau để hiểu rõ hơn sự khác biệt.

Tính năng null undefined
Ý nghĩa Thiếu vắng có chủ ý của bất kỳ giá trị đối tượng nào Biến đã được khai báo nhưng chưa gán giá trị
Kiểu Đối tượng Undefined
Trong JSON Hợp lệ Không hợp lệ
Tham số mặc định của hàm Không sử dụng làm mặc định Sử dụng làm mặc định cho tham số tùy chọn
So sánh null == undefined (đúng), null === undefined (sai) undefined == null (đúng), undefined === null (sai)

Hãy xem một số ví dụ mã để minh họa những khác biệt này:

// Kiểm tra kiểu
console.log(typeof null);       // Output: "object"
console.log(typeof undefined);  // Output: "undefined"

// T sérial hóa JSON
console.log(JSON.stringify({ a: null }));     // Output: {"a":null}
console.log(JSON.stringify({ a: undefined })); // Output: {}

// Tham số mặc định của hàm
function sayHello(name: string = "World") {
    console.log(`Hello, ${name}!`);
}

sayHello();        // Output: Hello, World!
sayHello(undefined); // Output: Hello, World!
sayHello(null);    // Output: Hello, null!

// So sánh
console.log(null == undefined);  // Output: true
console.log(null === undefined); // Output: false

Trong thực tế, sự lựa chọn giữa nullundefined thường phụ thuộc vào sở thích cá nhân hoặc của nhóm. Tuy nhiên, việc hiểu rõ sự khác biệt có thể giúp bạn viết mã chính xác hơn và giảm thiểu lỗi.

Dưới đây là một ví dụ cuối cùng để kết hợp tất cả:

function processUser(user: { name: string, age?: number } | null | undefined) {
    if (user === null) {
        console.log("User được đặt thành null");
    } else if (user === undefined) {
        console.log("User không được cung cấp");
    } else {
        console.log(`Xử lý người dùng: ${user.name}, Tuổi: ${user.age ?? "Unknown"}`);
    }
}

processUser(null);                    // Output: User được đặt thành null
processUser(undefined);               // Output: User không được cung cấp
processUser({ name: "Alice" });       // Output: Xử lý người dùng: Alice, Tuổi: Unknown
processUser({ name: "Bob", age: 30 }); // Output: Xử lý người dùng: Bob, Tuổi: 30

Hàm này minh họa cách chúng ta có thể xử lý null, undefined, và các đối tượng người dùng hợp lệ khác nhau trong một tình huống thực tế.

Và thế là xong! Bạn đã học được cách sử dụng nullundefined trong TypeScript. Nhớ rằng, thực hành là cách tốt nhất để thành thạo, vì vậy đừng ngần ngại thử nghiệm với các khái niệm này trong mã của riêng bạn. Chúc bạn may mắn và mã hóa vui vẻ, và mong rằng các biến của bạn luôn có chủ ý là null hoặc undefined!

Credits: Image by storyset