tsconfig.json là tệp cấu hình của một dự án TypeScript. Khi bạn chạy tsc không kèm đối số, trình biên dịch tìm tệp này trong thư mục hiện tại (rồi đến các thư mục cha), đọc các tùy chọn trong compilerOptions và kiểm tra những tệp mà include liệt kê. Một tệp nhỏ nhưng đầy đủ:
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
Cấu hình này biên dịch mọi tệp TypeScript trong src thành JavaScript trong dist, ở dạng code ES2022, theo quy tắc module của Node.js, với mọi kiểm tra nghiêm ngặt được bật. npx tsc --init tạo một tệp khởi đầu dài hơn, có chú thích cho từng tùy chọn.
Tệp này là JSON có chú thích: chú thích //, chú thích /* */ và dấu phẩy ở cuối đều được chấp nhận.
Cấu hình khởi đầu nên dùng
Cho một ứng dụng hoặc script Node.js:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"target": "es2024",
"lib": ["es2024"],
"module": "nodenext",
"types": ["node"],
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src"]
}
Cấu hình này cần npm install --save-dev @types/node để có kiểu của Node.js. Với module: "nodenext", trường "type" trong package.json quyết định một tệp .ts là ES module ("type": "module") hay CommonJS ("type": "commonjs", giá trị mà npm init -y ghi vào). Hãy dùng "type": "module" cho dự án mới có dùng import và export.
Với code do một bundler như Vite, esbuild hay webpack build, bundler viết ra JavaScript còn tsc chỉ kiểm tra:
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "dom"],
"module": "preserve",
"noEmit": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
Các bộ khởi tạo của framework (Vite, Next.js, Angular) tự tạo tsconfig.json riêng. Hãy bắt đầu từ tệp đó thay vì thay thế nó.
Các tùy chọn quan trọng
| Tùy chọn | Tác dụng | Mặc định trong TypeScript 7 |
|---|---|---|
target | Phiên bản JavaScript của đầu ra; cú pháp mới hơn được viết lại cho target cũ | es2025 |
lib | Kiểu của các API dựng sẵn mà trình kiểm tra biết (Array.prototype.at, Map, document) | Theo target, cộng thêm DOM |
module | Loại code module được xuất ra và quy tắc module được áp dụng | esnext |
moduleResolution | Cách tìm đường dẫn import trên ổ đĩa | nodenext hoặc node16 khi đi với module tương ứng, còn lại là bundler |
strict | Bật cả nhóm kiểm tra nghiêm ngặt | true |
rootDir | Thư mục có cấu trúc được sao lại vào outDir | Thư mục chứa tsconfig.json |
outDir | Nơi đặt các tệp .js (và .d.ts) | Cạnh mỗi tệp nguồn |
include / exclude / files | Những tệp nào thuộc dự án | Mọi tệp .ts trong thư mục |
types | Những package @types nào được nạp mà không cần import | [], không có |
noEmit | Chỉ kiểm tra, không ghi gì | false |
declaration | Ghi thêm tệp khai báo kiểu .d.ts | false |
sourceMap | Ghi tệp .js.map cho trình gỡ lỗi | false |
esModuleInterop | Cho phép import x from "cjs-package" hoạt động với package CommonJS | true (không tắt được) |
skipLibCheck | Bỏ qua kiểm tra kiểu các tệp .d.ts, kể cả trong node_modules | false |
target và lib
target cho biết đầu ra phải chạy trên phiên bản JavaScript nào. Cú pháp mới hơn target sẽ được viết lại: với "target": "es2017", một class field hay ??= được chuyển thành code cũ hơn. Target thấp nhất mà TypeScript 7 chấp nhận là es2015 (es6); es5 đã bị loại bỏ.
target không bổ sung các API lúc chạy còn thiếu. Mô tả chúng là việc của lib, còn cung cấp chúng là việc của polyfill. lib cho trình kiểm tra biết những đối tượng và phương thức dựng sẵn nào tồn tại. Mặc định của nó đi theo target và có cả kiểu DOM của trình duyệt, nên document vẫn qua kiểm tra kiểu ngay cả trong dự án Node.js, trừ khi bạn tự đặt lib. Dùng một phương thức thuộc chuẩn mới hơn lib của bạn là lỗi biên dịch:
src/b.ts(1,24): error TS2550: Property 'toSorted' does not exist on type 'number[]'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2023' or later.
Các ví dụ chạy được trong tài liệu này dùng lib es2022, nên toSorted, Object.groupBy và các phương thức mới của Set không có trong đó.
module và moduleResolution
module có ba giá trị hợp lý cho code mới:
nodenext: cho code mà Node.js chạy. Mỗi tệp là ESM hay CommonJS tùy theo phần mở rộng (.mts,.cts) hoặc trường"type"củapackage.jsongần nhất, đúng như cách Node quyết định. Import tương đối trong ES module cần phần mở rộng tệp, viết là.jsdù mã nguồn là.ts:import { add } from "./math.js".moduleResolutiontự động đi theo.preserve: cho code do bundler xử lý. Các import được giữ nguyên như khi viết, và việc phân giải dùng quy tắcbundler, cho phép import không có phần mở rộng.esnext: đầu ra ES module thuần, với cách phân giảibundler. Đây là mặc định khi không đặtmodule.
Một lỗi đầu tiên hay gặp với cấu hình của tsc --init đến từ việc npm init -y ghi "type": "commonjs":
src/main.ts(1,10): error TS1295: ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.
Tệp dùng import, nhưng package.json lại nói dự án là CommonJS. Đổi "type" thành "module". Trang về module trình bày chi tiết về import và export.
strict và các tùy chọn kiểm tra
"strict": true bật cùng lúc một nhóm kiểm tra: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, strictBuiltinIteratorReturn, noImplicitThis và useUnknownInCatchVariables. Đây là mặc định trong TypeScript 7 và được bật trong mọi ví dụ chạy được ở đây. Code viết cho chế độ strict sẽ thu hẹp kiểu trước khi dùng một giá trị có thể không tồn tại:
Không có chú thích kiểu, kiểu của tham số không thể suy ra được, và noImplicitAny báo lỗi:
index.ts(2,17): error TS7006: Parameter 'n' implicitly has an 'any' type.
strict không gồm mọi kiểm tra hữu ích. Có hai tùy chọn đáng thêm vào là noUncheckedIndexedAccess (truy cập một phần tử mảng hay một index signature cho ra kiểu T | undefined) và exactOptionalPropertyTypes (không thể gán tường minh undefined cho một thuộc tính tùy chọn); tsc --init bật cả hai. Các kiểm tra về phong cách như noUnusedLocals, noImplicitReturns và noFallthroughCasesInSwitch mặc định đều tắt.
Những tệp nào: include, exclude, rootDir, outDir
Khi không có include hay files, dự án gồm mọi tệp .ts, .tsx và .d.ts trong thư mục và các thư mục con. node_modules luôn bị loại ra. outDir cũng bị loại ra, nhưng chỉ khi bạn chưa đặt exclude: một danh sách exclude tự đặt sẽ thay thế mặc định đó, nên hãy thêm thư mục đầu ra vào danh sách. include và exclude nhận mẫu glob:
{
"include": ["src", "scripts/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
exclude chỉ lọc những gì include đã tìm thấy. Một tệp được một tệp khác trong dự án import vẫn được biên dịch.
outDir là nơi đặt đầu ra, còn rootDir là phần cây mã nguồn được sao lại vào đó: với "rootDir": "./src", src/api/users.ts trở thành dist/api/users.js. Hãy đặt cả hai. rootDir mặc định là thư mục chứa tsconfig.json, nên nếu bạn chỉ đặt outDir trong khi mã nguồn nằm trong src, TypeScript 7 dừng lại với lỗi:
error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
Tùy chọn đầu ra: noEmit, declaration, sourceMap
"noEmit": truebiếntscthành một trình kiểm tra kiểu thuần túy. Dùng nó khi một công cụ khác (bundler,tsx, tính năng bỏ kiểu của Node.js) tạo ra JavaScript."declaration": trueghi một tệp.d.tscho mỗi module, chỉ có kiểu mà không có code. Thư viện cần tùy chọn này để người dùng có được kiểu.declarationMapthêm các map để "go to definition" dẫn tới mã nguồn.ts."sourceMap": trueghi các tệp.js.mapđể trình gỡ lỗi và stack trace trỏ đúng dòng trong tệp.ts."noEmitOnError": truekhông ghi gì khi còn lỗi kiểu. Không có nó,tscvẫn báo lỗi và vẫn ghi ra JavaScript.
types, esModuleInterop và skipLibCheck
types liệt kê các package @types được nạp toàn cục, không cần import. Mặc định của TypeScript 7 là danh sách rỗng, nên sau npm install --save-dev @types/node bạn còn phải thêm "types": ["node"]; nếu không, process và require vẫn là tên không xác định (error TS2591: Cannot find name 'process'). Các trình chạy test có hàm toàn cục, như describe của Jest, cũng được thêm vào danh sách này.
esModuleInterop giúp default import từ package CommonJS hoạt động (import express from "express"). Tùy chọn này luôn bật trong TypeScript 7, và đặt nó thành false là lỗi.
skipLibCheck bỏ qua việc kiểm tra kiểu các tệp khai báo. Nó giúp build nhanh hơn và tránh những lỗi bên trong node_modules mà bạn không sửa được, đổi lại bạn sẽ không phát hiện xung đột giữa kiểu của hai thư viện. Hầu hết dự án đều bật nó.
Dùng chung thiết lập với extends
extends nạp một cấu hình khác và cho phép tệp này ghi đè một phần của nó:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
compilerOptions được gộp theo từng tùy chọn, còn include, exclude và files từ tệp gốc sẽ bị thay thế chứ không gộp nếu tệp này có đặt chúng. Đường dẫn trong tệp gốc được phân giải tương đối theo chính tệp gốc. extends cũng nhận một package: dự án @tsconfig/bases phát hành một cấu hình cho mỗi môi trường, ví dụ npm install --save-dev @tsconfig/node24 rồi "extends": "@tsconfig/node24/tsconfig.json".
Để xem thiết lập cuối cùng sau khi đã áp dụng mọi extends và giá trị mặc định, chạy:
npx tsc --showConfig
Các tùy chọn bị loại bỏ trong TypeScript 7
TypeScript 6 đánh dấu lỗi thời các thiết lập này và TypeScript 7 đã loại bỏ chúng. Một cấu hình vẫn dùng một trong số đó sẽ thất bại với error TS5108 hoặc TS5102, ví dụ Option 'baseUrl' has been removed. Please remove it from your configuration.
| Thiết lập bị loại bỏ | Dùng thay thế |
|---|---|
"target": "es5" | es2015 trở lên |
"moduleResolution": "node" (node10) hoặc "classic" | nodenext hoặc bundler |
"module": "amd", "umd", "system", "none" | nodenext, esnext hoặc preserve, và một bundler cho các định dạng khác |
baseUrl | Các mục paths tương đối theo tệp tsconfig |
outFile | Một bundler |
downlevelIteration | Không cần gì: các target từ es2015 trở lên hỗ trợ sẵn việc lặp |
"esModuleInterop": false, "allowSyntheticDefaultImports": false, "alwaysStrict": false | Xóa dòng đó; các tùy chọn này luôn bật |
Trang TypeScript 7 liệt kê các thay đổi khác trong phiên bản đó, kể cả những giá trị mặc định mới mà bảng này không đề cập.
Câu hỏi thường gặp
tsconfig.json là gì?
Đây là tệp cấu hình của một dự án TypeScript. Sự có mặt của tệp này đánh dấu thư mục là gốc của dự án, compilerOptions quy định cách trình biên dịch kiểm tra và xuất code, còn include, exclude hoặc files cho biết những tệp nào thuộc dự án. Chạy tsc không kèm đối số sẽ đọc tệp này.
Làm sao để tạo tệp tsconfig.json?
Chạy npx tsc --init trong thư mục dự án (khi đã cài TypeScript). Lệnh này tạo một tsconfig.json với các thiết lập nên dùng và một chú thích cho từng tùy chọn. Bạn cũng có thể tự viết tệp này; {} là một tsconfig hợp lệ, dùng mọi giá trị mặc định.
Nên đặt target trong tsconfig là gì?
Phiên bản JavaScript cũ nhất mà code của bạn phải chạy được. Với Node.js hiện nay, es2022 trở lên là an toàn; mặc định của TypeScript 7 là es2025. target quyết định cú pháp mới nào được viết lại cho các engine cũ, đồng thời chọn lib mặc định, tức tập API dựng sẵn mà trình kiểm tra kiểu biết đến.
module và moduleResolution khác nhau thế nào?
module quyết định loại code module mà trình biên dịch viết ra (ES import/export hay CommonJS require) và quy tắc module nào được áp dụng. moduleResolution quyết định cách tìm một đường dẫn import như "./utils.js" hay "lodash" trên ổ đĩa. Dùng nodenext cho code mà Node.js chạy, và module: "preserve" (kéo theo cách phân giải bundler) cho code do bundler xử lý.
tsconfig.json có chứa chú thích được không?
Được. Trình biên dịch đọc tệp này như JSON có chú thích: chú thích // và /* */ cùng dấu phẩy ở cuối đều hợp lệ, vì vậy tsc --init tạo ra một tệp đầy những tùy chọn đã bị chú thích lại. Những công cụ khác đọc tệp như JSON nghiêm ngặt có thể báo lỗi vì chúng.