Menu

Strict mode trong TypeScript: strict: true bật những gì

strict: true trong tsconfig.json bật cả một nhóm kiểm tra kiểu: noImplicitAny, strictNullChecks, strictPropertyInitialization và năm cờ khác. Xem mỗi cờ bắt được lỗi gì, cách bật strict mode trong một dự án có sẵn, và những cờ hữu ích mà strict không bao gồm.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

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àoLỗi
noImplicitAnymột tham số hoặc biến có kiểu sẽ âm thầm thành anyTS7006 Parameter 'x' implicitly has an 'any' type.
strictNullChecksdùng một giá trị có thể là null hoặc undefinedTS18048 'u' is possibly 'undefined'.
strictFunctionTypesgán một hàm có kiểu tham số hẹp hơn mức yêu cầuTS2322
strictBindCallApplytruyền sai đối số cho .call, .bind và .applyTS2345
strictPropertyInitializationmột trường của lớp không bao giờ được gánTS2564 Property 'name' has no initializer and is not definitely assigned in the constructor.
noImplicitThisthis có kiểu any ngầm định, như trong một function lồng nhauTS2683
useUnknownInCatchVariablesdùng biến của catch trước khi thu hẹp kiểu (nó là unknown)TS18046 'err' is of type 'unknown'.
strictBuiltinIteratorReturncoi 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:

  1. Thêm "strict": true và tắt những cờ gây nhiều lỗi nhất, thường là "strictNullChecks": false và "noImplicitAny": false.
  2. Sửa các lỗi còn lại, rồi bật thêm một cờ và lặp lại.
  3. Với noImplicitAny, phần lớn cách sửa là thêm chú thích kiểu cho tham số. Với strictNullChecks, thêm | undefined ở nơi giá trị có thể thiếu, rồi xử lý bằng ?., ?? hoặc một câu lệnh if.
  4. Ở chỗ chưa thể sửa ngay, đặt // @ts-expect-error kè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
noUncheckedIndexedAccessarr[i] và record[key] có thêm undefined trong kiểu
exactOptionalPropertyTypesdebug?: boolean chấp nhận thiếu khóa nhưng không chấp nhận debug: undefined
noImplicitReturnsmọi nhánh code của một hàm có giá trị trả về đều phải trả về (TS7030)
noImplicitOverridemột phương thức ghi đè phương thức của lớp cha phải ghi override (TS4114)
noFallthroughCasesInSwitchmột case không rỗng phải kết thúc bằng break, return hoặc throw (TS7029)
noUnusedLocals, noUnusedParametersbiến và tham số không dùng đến bị báo lỗi
noPropertyAccessFromIndexSignaturekhó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.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu