Strict mode là tùy chọn "strict": true trong tsconfig.json. Đây là một công tắc cho tám cờ kiểm tra kiểu, bắt các trường hợp any ngầm định, null và undefined không được kiểm tra, phép gán hàm không an toàn và trường của lớp chưa được khởi tạo. Trong TypeScript 7, strict bật mặc định. Code vượt qua được strict trông như thế này:
Đầu ra là no user, SyntaxError và 42. Bỏ : number, ?. hoặc phép kiểm tra instanceof là tệp không còn biên dịch được nữa.
Bật strict mode
{
"compilerOptions": {
"strict": true
}
}
Từng cờ riêng lẻ luôn thắng strict, theo cả hai chiều. "strict": true, "strictNullChecks": false giữ mọi thứ trừ kiểm tra null; "strict": false, "noImplicitAny": true chỉ bật đúng cờ đó. strict cũng tự động bật cho bạn các kiểm tra được thêm vào ở những phiên bản sau, vì các cờ mới thuộc nhóm strict sẽ gia nhập nhóm này.
strict của TypeScript không liên quan gì đến chỉ thị "use strict" của JavaScript, vốn là một chế độ lúc chạy. TypeScript 7 luôn sinh "use strict" ở nơi cần thiết, và đặt alwaysStrict: false là lỗi TS5108 (tùy chọn này đã bị loại bỏ).
Mỗi cờ bắt được lỗi gì
| Cờ | Báo lỗi khi nào | Lỗi |
|---|---|---|
noImplicitAny | một tham số hoặc biến có kiểu sẽ âm thầm thành any | TS7006 Parameter 'x' implicitly has an 'any' type. |
strictNullChecks | dùng một giá trị có thể là null hoặc undefined | TS18048 'u' is possibly 'undefined'. |
strictFunctionTypes | gán một hàm có kiểu tham số hẹp hơn mức yêu cầu | TS2322 |
strictBindCallApply | truyền sai đối số cho .call, .bind và .apply | TS2345 |
strictPropertyInitialization | một trường của lớp không bao giờ được gán | TS2564 Property 'name' has no initializer and is not definitely assigned in the constructor. |
noImplicitThis | this có kiểu any ngầm định, như trong một function lồng nhau | TS2683 |
useUnknownInCatchVariables | dùng biến của catch trước khi thu hẹp kiểu (nó là unknown) | TS18046 'err' is of type 'unknown'. |
strictBuiltinIteratorReturn | coi it.next().value của một iterator có sẵn là luôn có giá trị | TS2322 |
Hai cờ làm thay đổi nhiều code nhất là noImplicitAny và strictNullChecks. Khối code này cố tình vi phạm cả hai:
Nó in ra index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. và index.ts(11,13): error TS18048: 'user' is possibly 'undefined'. Không có strict, đoạn code này biên dịch được rồi crash lúc chạy với TypeError: Cannot read properties of undefined (reading 'name'). Có strict, lỗi crash đó trở thành lỗi biên dịch. Cách sửa chính là khối code đầu tiên trên trang này.
strictFunctionTypes: vì sao cần nó
Một hàm chỉ xử lý chuỗi không được dùng ở nơi có thể nhận vào số. Phép kiểm tra bên dưới được tắt bằng @ts-expect-error để bạn chạy thử và thấy lỗi này ngăn chặn điều gì:
Không có dòng chú thích đó, phép gán là lỗi TS2322, Type '(s: string) => void' is not assignable to type 'Handler'., kèm theo Types of parameters 's' and 'value' are incompatible. Vẫn còn một ngoại lệ có chủ đích: tham số của các phương thức khai báo bằng cú pháp phương thức (handle(value: string | number): void bên trong một interface) vẫn được kiểm tra theo cách lỏng hơn, nên shout có thể gán cho phương thức như vậy mà không báo lỗi.
strictPropertyInitialization
Mọi trường của lớp phải nhận giá trị ngay khi khai báo hoặc trong hàm khởi tạo. Có ba cách để thỏa mãn điều này:
Đầu ra:
Account {
owner: 'Ada',
balance: 0,
history: [],
lastLogin: undefined,
sessionId: 's-1'
}
Hai thuộc tính lastLogin và sessionId tồn tại với giá trị undefined vì trường của lớp là trường JavaScript thật khi target là ES2022. Dấu ! bỏ phép kiểm tra mà không thêm bất kỳ cơ chế bảo vệ nào lúc chạy, nên hãy ưu tiên các cách còn lại. Cờ này cần strictNullChecks; tắt cờ đó thì cờ này cũng bị tắt theo.
Bật strict trong một dự án có sẵn
Chuyển cả một codebase lớn sang strict cùng lúc có thể sinh ra hàng trăm lỗi. Vì strict là mặc định của TypeScript 7, nâng cấp một dự án mà tsconfig.json chưa từng nhắc đến strict sẽ tự bật nó; hãy viết "strict": false nếu bạn cần giữ hành vi cũ trong lúc chuyển đổi. Một lộ trình giúp bản build luôn thành công:
- Thêm
"strict": truevà tắt những cờ gây nhiều lỗi nhất, thường là"strictNullChecks": falsevà"noImplicitAny": false. - Sửa các lỗi còn lại, rồi bật thêm một cờ và lặp lại.
- Với
noImplicitAny, phần lớn cách sửa là thêm chú thích kiểu cho tham số. VớistrictNullChecks, thêm| undefinedở nơi giá trị có thể thiếu, rồi xử lý bằng?.,??hoặc một câu lệnhif. - Ở chỗ chưa thể sửa ngay, đặt
// @ts-expect-errorkèm lý do trên dòng đó. Khác với@ts-ignore, nó sẽ báo lỗi khi vấn đề đã hết, nên danh sách tự ngắn lại.
Đừng dùng as any hay ! để tắt lỗi hàng loạt: mỗi lần như vậy lại che đúng con bug mà cờ đó được thêm vào để tìm.
Những cờ hữu ích mà strict không bao gồm
Các cờ này tách riêng vì chúng từ chối cả những đoạn code thường là đúng. Dù vậy, nhiều dự án vẫn bật chúng.
| Cờ | Tác dụng |
|---|---|
noUncheckedIndexedAccess | arr[i] và record[key] có thêm undefined trong kiểu |
exactOptionalPropertyTypes | debug?: boolean chấp nhận thiếu khóa nhưng không chấp nhận debug: undefined |
noImplicitReturns | mọi nhánh code của một hàm có giá trị trả về đều phải trả về (TS7030) |
noImplicitOverride | một phương thức ghi đè phương thức của lớp cha phải ghi override (TS4114) |
noFallthroughCasesInSwitch | một case không rỗng phải kết thúc bằng break, return hoặc throw (TS7029) |
noUnusedLocals, noUnusedParameters | biến và tham số không dùng đến bị báo lỗi |
noPropertyAccessFromIndexSignature | khóa từ index signature phải dùng obj["key"], không dùng obj.key |
noUncheckedIndexedAccess bắt được nhiều bug thật nhất. Khi bật nó:
const scores = [90, 85];
const d: Record<string, number> = {};
const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"]; // same error
const safe = scores[2] ?? 0; // number
for (const s of scores) { // for...of is not affected: s is number
console.log(s);
}
tsc --init trong TypeScript 7 bật noUncheckedIndexedAccess và exactOptionalPropertyTypes trong tệp cấu hình mà nó tạo ra, bên cạnh strict.
Câu hỏi thường gặp
Strict mode trong TypeScript làm gì?
"strict": true bật một nhóm kiểm tra chặt chẽ hơn: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables và strictBuiltinIteratorReturn. Cùng nhau, chúng ngăn any xuất hiện một cách âm thầm và đưa null cùng undefined vào hệ thống kiểu.
Strict mode có bật mặc định trong TypeScript không?
Trong TypeScript 7 thì có: strict mặc định là true, nên một dự án không đặt strict sẽ có mọi kiểm tra strict. tsc --init cũng ghi rõ "strict": true. Muốn tắt, bạn phải viết "strict": false.
Có thể tắt một kiểm tra strict mà vẫn giữ các kiểm tra còn lại không?
Có. Từng cờ riêng lẻ sẽ ghi đè strict: { "strict": true, "strictNullChecks": false } giữ mọi kiểm tra strict trừ kiểm tra null. Đây là cách thường dùng để chuyển một codebase lớn sang strict, từng cờ một.
Strict mode của TypeScript có giống "use strict" của JavaScript không?
Không. "use strict" là một chế độ lúc chạy của JavaScript, thay đổi cách code hoạt động. strict của TypeScript chỉ thay đổi những gì trình kiểm tra kiểu báo ra. TypeScript 7 luôn sinh "use strict" cho đầu ra không phải module, và alwaysStrict: false giờ là một tùy chọn đã bị loại bỏ.
strict có bao gồm noUncheckedIndexedAccess không?
Không. noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitReturns, noImplicitOverride và noFallthroughCasesInSwitch là các cờ riêng mà bạn tự bật. tsc --init bật hai cờ đầu trong tệp cấu hình mà nó tạo ra.