TypeScript 編譯配置 tsconfig.json compilerOptions 全解析
由於 tsconfig.json 中的 compilerOptions 可配置選項實在是太多了,常常會忘記一些選項的用途,但 TypeScript 的官方文件有些選項要不是寫得太冗長就是太簡短。因此決定一次將所有 compilerOptions 透過 chatgpt 整理成一個大抄,方便我日後快速查詢。
- Type Checking
- allowUnreachableCode
- allowUnusedLabels
- alwaysStrict
- exactOptionalPropertyTypes
- noFallthroughCasesInSwitch
- noImplicitAny
- noImplicitOverride
- noImplicitReturns
- noImplicitThis
- noPropertyAccessFromIndexSignature
- noUncheckedIndexedAccess
- noUnusedLocals
- noUnusedParameters
- strict
- strictBindCallApply
- strictFunctionTypes
- strictNullChecks
- strictPropertyInitialization
- useUnknownInCatchVariables
- Modules
- Emit
- JavaScript Support
- Editor Support
- Interop Constraints
- Backwards Compatibility
- Language and Environment
- Compiler Diagnostics
- Projects
- Output Formatting
- Completeness
- Watch Options
- Reference
Type Checking
allowUnreachableCode
設定是否允許在程式中存在不可達的程式碼。
說明範例:
function example1() {
return;
console.log("這段程式永遠不會被執行"); // 如果 allowUnreachableCode 為 true,則不會報錯
}
allowUnusedLabels
設定是否允許在程式中存在未使用的標籤。
說明範例:
unusedLabel: // 如果 allowUnusedLabels 為 true,則不會報錯
for (let i = 0; i < 10; i++) {
if (i === 5) {
break;
}
}
alwaysStrict
設定是否在 ECMAScript 嚴格模式下進行解析,在編譯輸出的 JavaScript 文件中強制啟用嚴格模式(插入 “use strict”)。
說明範例:
function example2() {
// "use strict"; 自動插入於此
let x = 1;
delete x; // 在嚴格模式下,這將會拋出錯誤
}
exactOptionalPropertyTypes
設定是否精確控制可選屬性類型,即是否允許 undefined
類型的值被賦給可選屬性。
說明範例:
interface User {
name: string;
age?: number;
}
const user1: User = { name: "Alice" };
const user2: User = { name: "Bob", age: undefined }; // 如果 exactOptionalPropertyTypes 為 true,則將報錯
noFallthroughCasesInSwitch
設定是否在 switch 語句中禁止 case 子句的穿透行為。
說明範例:
function example3(x: number) {
switch (x) {
case 1:
console.log("1");
// 沒有 break 語句會導 致穿透,若 noFallthroughCasesInSwitch 為 true,則這將報錯
case 2:
console.log("2");
break;
default:
console.log("default");
}
}
noImplicitAny
設定是否在變數或參數的類型未明確指定時報錯。
說明範例:
function example1(x) {
// 如果 noImplicitAny 為 true,這將報錯,因為 x 的類型未指定
console.log(x);
}
noImplicitOverride
設定是否要求在覆蓋基類方法時必須明確使用 override
關鍵字。
說明範例:
class Base {
greet() {
console.log("Hello from Base");
}
}
class Derived extends Base {
override greet() { // 如果 noImplicitOverride 為 true,這裡必須加上 override,否則報錯
console.log("Hello from Derived");
}
}
noImplicitReturns
設定是否在函數的所有程式碼路徑未明確返回值時報錯。
說明範例:
function example2(condition: boolean): number {
if (condition) {
return 1;
}
// 如果 noImplicitReturns 為 true,這裡將報錯,因為並非所有路徑都有返回值
}
noImplicitThis
設定是否在 this
表示式的值為 any
類型時報錯。
說明範例:
class Example {
value: number = 0;
logValue() {
console.log(this.value);
}
method() {
[1, 2, 3].forEach(function () {
console.log(this.value); // 如果 noImplicitThis 為 true,這將報錯,因為 this 的類型未明確指定
});
}
}
noPropertyAccessFromIndexSignature
這個選項確保通過“點”語法 (obj.key
) 和“索引”語法 (obj["key"]
) 訪問屬性的一致性,並要求以索引方式訪問來自索引簽名的屬性。
說明範例:
interface GameSettings {
// 明確定義的屬性
speed: "fast" | "medium" | "slow";
quality: "high" | "low";
// 索引簽名,表示其他未定義的屬性均為字符串類型
[key: string]: string;
}
const settings: GameSettings = {
speed: "fast",
quality: "high",
username: "player1"
};
console.log(settings.speed); // 正常訪問
console.log(settings.quality); // 正常訪問
// 如果 noPropertyAccessFromIndexSignature 為 true,以下將報錯
console.log(settings.username);
// Property 'username' comes from an index signature, so it must be accessed with ['username'].
// 正確的訪問方式應為
console.log(settings["username"]); // 正常訪問
noUncheckedIndexedAccess
設定是否在通過索引簽名訪問 屬性時進行嚴格檢查。啟用後,索引簽名的屬性訪問將返回可能 undefined
的值。
說明範例:
interface MyObject {
[key: string]: string;
}
const obj: MyObject = {
knownProperty: "value"
};
const value = obj["unknownProperty"]; // 如果 noUncheckedIndexedAccess 為 true,value 的類型將是 string | undefined
if (value !== undefined) {
console.log(value.toUpperCase()); // 這樣可以避免對 undefined 調用方法
}
noUnusedLocals
設定是否在程式中存在未使用的本地變量時報錯。
說明範例:
function example1() {
const unusedVariable = 42; // 如果 noUnusedLocals 為 true,這將報錯,因為未使用
const usedVariable = 24;
console.log(usedVariable); // 這是使用過的變量,沒問題
}
noUnusedParameters
設定是否在函數中存在未使用的參數時報錯。
說 明範例:
function example2(unusedParam: number, usedParam: number) {
// 如果 noUnusedParameters 為 true,unusedParam 將報錯,因為未使用
console.log(usedParam); // 這是使用過的參數,沒問題
}
strict
設定是否啟用所有嚴格類型檢查選項。這是一個總開關,用於啟用各種嚴格模式的檢查。
Related Rules:
alwaysStrict
strictNullChecks
strictBindCallApply
strictFunctionTypes
strictPropertyInitialization
noImplicitAny
noImplicitThis
useUnknownInCatchVariables
strictBindCallApply
設定是否對 Function.prototype.bind
, call
, 和 apply
方法進行嚴格檢查。
說明範例:
function example4(a: number, b: string) {
console.log(a, b);
}
const boundFunction = example4.bind(null, 42); // 在 strictBindCallApply 為 true 時,bind 的參數必須匹配原始函數
boundFunction("hello"); // 正常工作
// 如果 bind, call, apply 的參數類型不匹配,則會報錯
example4.call(null, 42, "hello"); // 正常工作
example4.apply(null, [42, "hello"]); // 正常工作
strictFunctionTypes
設定是否啟用嚴格的函數類型 檢查。這會更嚴格地檢查函數參數和返回值的類型相容性。
說明範例:
type Handler = (a: number) => void;
function example1(handler: Handler) {
handler(42);
}
const validHandler: Handler = (a) => console.log(a);
example1(validHandler); // 正常工作
const invalidHandler = (a: string) => console.log(a);
// 如果 strictFunctionTypes 為 true,這將報錯,因為參數類型不匹配
// example1(invalidHandler);
strictNullChecks
設定是否啟用嚴格的空值檢查。啟用後,null 和 undefined 不能賦值給其他類型,除非明確允許。
說明範例:
function example2(value: string | null) {
if (value !== null) {
console.log(value.toUpperCase()); // 在 strictNullChecks 為 true 時,需要檢查 null
}
}
let nullableString: string | null = "Hello";
// nullableString = null; // 正常工作
let nonNullableString: string = "Hello";
// nonNullableString = null; // 如果 strictNullChecks 為 true,這將報錯
strictPropertyInitialization
設定是否啟用嚴格的屬性初始化檢查。啟用後,類別的所有屬性必須在構造函數中初始化,或者在定義時初始化。
說明範例:
class Example3 {
initializedProperty: string = "Hello";
uninitializedProperty: string; // 如果 strictPropertyInitialization 為 true,這將報錯
constructor() {
this.uninitializedProperty = "World"; // 正常初始化
}
}
useUnknownInCatchVariables
設定是否在 catch 子句的變量中使用 unknown
類型,而不是 any
類型。這可以提高類型安全性,強制開發者處理未知類型。
說明範例:
try {
throw new Error("Something went wrong");
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message); // 在 useUnknownInCatchVariables 為 true 時,需要檢查 error 的類型
}
}
Modules
allowArbitraryExtensions
這個選項允許導入路徑中包含非標準擴展名的文件。當一個導入路徑以一個非 JavaScript 或 TypeScript 文件擴展名結尾時,編譯器將會查找該路徑的聲明文件,格式為 {file basename}.d.{extension}.ts
。
說明範例:
假設你有一個 CSS 文件和對應的 TypeScript 聲明文件:
/* app.css */
.cookie-banner {
display: none;
}
對應的聲明文件:
// app.d.css.ts
declare const css: {
cookieBanner: string;
};
export default css;
在 TypeScript 文件中導入這個 CSS 文件:
// App.tsx
import styles from "./app.css"; // 如果 allowArbitraryExtensions 為 true,這將被允許
console.log(styles.cookieBanner); // "string"
allowImportingTsExtensions
這個選項允許 TypeScript 文件之間使用 TypeScript 特有的擴展名(如 .ts
, .mts
, 或 .tsx
)進行導入。
此選項僅在 --noEmit
或 --emitDeclarationOnly
啟用時允許,因為這些導入路徑在 JavaScript 輸出文件中是無法解析的。預期情況下,你的解析器(例如你的打包工具、運行時或其他工具)將使這些 .ts
文件之間的導入能夠正常工作。
說明範例:
假設你有兩個 TypeScript 文件:
// utils.ts
export const foo = () => "Hello, World!";
在另一個 TypeScript 文件中,你可以顯式地包含擴展名進行導入:
// index.ts
import { foo } from "./utils.ts"; // 如果 allowImportingTsExtensions 為 true,這將被允許
console.log(foo());
allowUmdGlobalAccess
當設定為 true
時,允許你在模塊文件內部訪問 UMD 模塊的全局變量。模塊文件是指包含導入和/或導出的文件。沒有這個選項時,使用 UMD 模塊的導出需要一個導入聲明。
說明範例:
假設你使用一個 UMD 模塊:
// utils.js (UMD 模塊)
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node
module.exports = factory();
} else {
// 全局變量
root.myLibrary = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
return {
foo: function () {
return "Hello, World!";
}
};
}));
在 TypeScript 文件中,你可以直接使用這個全局變量:
// index.ts
declare var myLibrary: any;
console.log(myLibrary.foo()); // 如果 allowUmdGlobalAccess 為 true,這將被允許
baseUrl
設定相對模塊導入的基礎路徑。這個選項主要用於解析非絕對路徑的模塊,允許你在項目中使用更簡潔的導入路徑。
說明範例:
假設你的項目結構如下:
src/
components/
Header.ts
utils/
helpers.ts
tsconfig.json
在 tsconfig.json
中設定 baseUrl
為 src
:
{
"compilerOptions": {
"baseUrl": "src"
}
}
這樣,你可以在 Header.ts
中使用相對於 src
目錄的路徑來導入模塊:
// src/components/Header.ts
import { helperFunction } from "utils/helpers"; // 如果 baseUrl 設定為 src,這將被允許
這樣的配置使導入路徑更加簡潔,避免了使用相對路徑(例如 ../../utils/helpers
)的麻煩。
customConditions
customConditions
允許你指定一些額外的條件,這些條件在 TypeScript 解析 package.json
的 exports
或 imports
字段時應該被考慮。這些條件會添加到解析器默認使用的條件列表中。
說明範例:
假設在你的 tsconfig.json
中設定了 customConditions
:
{
"compilerOptions": {
"target": "es2022",
"moduleResolution": "bundler",
"customConditions": ["my-condition"]
}
}
這樣,在引用 package.json
中的 exports
或 imports
字段時,TypeScript 會考慮名為 my-condition
的條件。
例如,你的 package.json
文件包含以下內容:
{
"name": "my-package",
"version": "1.0.0",
"exports": {
".": {
"my-condition": "./foo.mjs",
"node": "./bar.mjs",
"import": "./baz.mjs",
"require": "./biz.mjs"
}
}
}
在 TypeScript 文件中進行導入時:
// main.ts
import myPackage from "my-package";
// TypeScript 將根據 `my-condition` 解析到 `./foo.mjs`
設定 customConditions
後,TypeScript 在解析 exports
或 imports
字段時,會優先考慮匹配 my-condition
的文件。例如,上面的配置會讓 TypeScript 在解析 my-package
時選擇 ./foo.mjs
文件,而不是其他條件匹配的文件。
這樣的配置允許你在模塊解析過程中使用自定義條件,以適應不同的運行環境或配置需求。這對於需要在不同環境中使用不同導出文件的應用程序特別有用。例如,你可以根據不同的條件提供不同的模塊版本,從而提高靈活性和適應性。
module
module
設定 TypeScript 編譯後的 JavaScript 模組格式,適用於不同的運行環境和需求。以下是主要的幾個選項及其適用場景:
- CommonJS
-
適用於 Node.js 環境。
-
生成 CommonJS 模組格式。
-
範例:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_1 = require("./constants");
exports.twoPi = constants_1.valueOfPi * 2;
-
- UMD
-
適用於能在多種環境(如 Node.js、AMD 模組系統、全域變數)中運行的程式碼。
-
範例:
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
} else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./constants"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_1 = require("./constants");
exports.twoPi = constants_1.valueOfPi * 2;
});
-
- AMD
-
適用於瀏覽器環境中的非同步模組定義。
-
範例:
define(["require", "exports", "./constants"], function (require, exports, constants_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
exports.twoPi = constants_1.valueOfPi * 2;
});
-
- System
-
使用 SystemJS 載入模組。
-
範例:
System.register(["./constants"], function (exports_1, context_1) {
"use strict";
var constants_1, twoPi;
var __moduleName = context_1 && context_1.id;
return {
setters: [
function (constants_1_1) {
constants_1 = constants_1_1;
}
],
execute: function () {
exports_1("twoPi", twoPi = constants_1.valueOfPi * 2);
}
};
});
-
- ESNext、ES2022
-
適用於現代 JavaScript 環境,生成 ES 模組格式。
-
支援最新的 JavaScript 特性,如動態匯入 (
import()
) 和頂層 await。 -
範例:
import { valueOfPi } from "./constants";
export const twoPi = valueOfPi * 2;
-
- node16、nodenext
-
從 TypeScript 4.7 開始支援,與 Node.js 的原生 ECMAScript 模塊支持集成,根據文件副檔名和最近的
package.json
中的type
設定來選擇生成 CommonJS 或 ES2020 格式 -
範例:
import { valueOfPi } from "./constants";
export const twoPi = valueOfPi * 2;
-
- preserve
-
在
--module preserve
模式下,保留原始的 ECMAScript 匯入和匯出語句,混合使用 CommonJS 和 ECMAScript 模塊語句。 -
範例:
import { valueOfPi } from "./constants";
const constants = require("./constants");
export const piSquared = valueOfPi * constants.valueOfPi;
-
moduleResolution
moduleResolution
設定 TypeScript 在編譯過程中如何解析模組匯入路徑的策略。不同的策略適用於不同的運行環境和需求。以下是主要的幾個選項及其適用場景:
- node16 或 nodenext
- 適用於現代版本的 Node.js。
- Node.js 12 及更高版本同時支援 ECMAScript imports 和 CommonJS require 的不同解析演算法。
- 與相應的
module
值結合時,根據 Node.js 在輸出 JavaScript 程式碼中看到的匯入或 require,選擇正確的解析演算法。
- node10
- 適用於 Node.js 10 之前的版本,只支援 CommonJS require。
- 在現代程式碼中可能不需要使用
node10
。
- bundler
- 適用於打包工具。
- 像
node16
和nodenext
一樣,此模式支援package.json
中的imports
和exports
欄位。 - 與 Node.js 解析模式不同的是,打包工具在匯入的相對路徑上不需要文件擴展名。
- classic
- TypeScript 1.6 發佈前使用的解析模式。
- 不推薦使用
classic
。
moduleSuffixes
moduleSuffixes
允許你為匯入模組指定一個後綴列表。TypeScript 會依次嘗試這些後綴,直到找到匹配的模組。
說明範例:
假 設你有以下文件結構:
src/
utils.ts
utils.test.ts
tsconfig.json
在 tsconfig.json
中配置 moduleSuffixes
:
{
"compilerOptions": {
"moduleSuffixes": [".test", ""]
}
}
這樣,當你匯入 ./utils
時,TypeScript 會先嘗試 ./utils.test.ts
,如果找不到,則會嘗試 ./utils.ts
:
// main.ts
import { foo } from "./utils"; // 這將優先匹配 utils.test.ts,其次是 utils.ts
noResolve
noResolve
配置選項禁止 TypeScript 自動解析模組匯入和引用。默認情況下,TypeScript 會檢查初始文件集中的匯入和 <reference>
指令,並將這些解析後的文件新增到你的程序中。儘管如此,TypeScript 仍會檢查匯入語句以確保它們解析為有效模組。
說明範例:
假設你有以下文件結構:
src/
main.ts
utils.ts
tsconfig.json
在 tsconfig.json
中配置 noResolve
:
{
"compilerOptions": {
"noResolve": true}
}
TypeScript 文件:
// main.ts
import { foo } from "./utils"; // TypeScript 不會解析和編譯 utils.ts
paths
paths
配置選項允許你為模組匯入指定自訂路徑對應。這對於重構項目結構或簡化模組匯入特別有用。
說明範例:
假設你有以下文件結構:
src/
components/
Header.ts
utils/
helpers.ts
tsconfig.json
在 tsconfig.json
中配置 paths
:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@utils/*": ["utils/*"]
}
}
}
這樣,你可以使用別名來匯入模組:
// main.ts
import { Header } from "@components/Header";
import { helperFunction } from "@utils/helpers";
resolveJsonModule
resolveJsonModule
允許你匯入 .json
文件並將其內容作為模組使用。這對於需要使用 JSON 配置或資料文件的項目特別有用。
說明範例:
假設你有以下文件結構:
src/
config.json
tsconfig.json
在 tsconfig.json
中配置 resolveJsonModule
:
{
"compilerOptions": {
"resolveJsonModule": true}
}
JSON 文件:
// config.json
{
"apiEndpoint": "https://api.example.com",
"timeout": 5000
}
TypeScript 文件:
// main.ts
import config from "./config.json";
console.log(config.apiEndpoint); // "https://api.example.com"
console.log(config.timeout); // 5000
resolvePackageJsonExports
resolvePackageJsonExports
選項讓 TypeScript 在解析模塊時考慮 package.json
文件中的 exports
字段。這對於現代 JavaScript 包結構非常有用,能夠正確處理不同的導入格式。
默認設置:
- 當
moduleResolution
設置為node16
、nodenext
或bundler
時,默認為true
。 - 否則默認為
false
。
說明範例:
假設 package.json
包含以下 exports
字段:
{
"name": "my-package",
"exports": {
".": {
"import": "./esm/index.js",
"require": "./cjs/index.js"
}
}
}
這樣,當你導入這個包時,TypeScript 會根據 exports
字段選擇正確的模塊文件。
resolvePackageJsonImports
resolvePackageJsonImports
選項讓 TypeScript 在解析以 #
開頭的導入時,考慮 package.json
文件中的 imports
字段。這對於自定義路徑映射非常有用。
默認設置:
- 當
moduleResolution
設置為node16
、nodenext
或bundler
時,默認為true
。 - 否則默認為
false
。
說明範例:
假設 package.json
包含以下 imports
字段:
{
"name": "my-project",
"imports": {
"#utils/*": "./src/utils/*"
}
}
這樣,當你使用 #utils/helpers
這樣的導入時,TypeScript 會根據 imports
字段解析到正確的文件。
rootDir
rootDir
用於指定編譯輸入文件的根目錄,TypeScript 會保持輸入文件的目錄結構在輸出目錄中不變。
默認設置:
- 所有非聲明文件的最長公共路徑。如果設置了
composite
,默認為包含tsconfig.json
的目錄。
說明範例:
假設項目結構如下:
MyProj
├── tsconfig.json
├── core
│ ├── a.ts
│ ├── b.ts
│ ├── sub
│ │ ├── c.ts
默認情況下,rootDir
為 core/
。如果希望輸出保留 core
目錄,可以在 tsconfig.json
中設置 rootDir
為 .
:
這樣,編譯後的文件結構將包含 core
目錄:
MyProj
├── dist
│ ├── core
│ │ ├── a.js
│ │ ├── b.js
│ │ ├── sub
│ │ │ ├── c.js
rootDirs
rootDirs
允許你將多個目錄視為同一虛擬目錄結構的一部分,這對於多源目錄項目中特別有用。
說明範例:
假設項目結構如下:
src
└── views
└── view1.ts
└── view2.ts
generated
└── templates
└── views
└── template1.ts
在 tsconfig.json
中配置 rootDirs
:
{
"compilerOptions": {
"rootDirs": ["src/views", "generated/templates/views"]
}
}
這樣,TypeScript 會將 src/views
和 generated/templates/views
視為同一虛擬目錄結構的一部分,允許模塊在這兩個目錄之間相互導入。
TypeScript 文件:
// src/views/view1.ts
import { template1 } from "./template1"; // 可以導入 generated/templates/views/template1.ts
typeRoots
typeRoots
用於指定 TypeScript 查找類型定義文件的根目錄。這個選項允許你自定義類型定義文件的搜索範圍,而不是使用默認的 node_modules/@types
目錄。
說明範例:
假設你的項目結構如下:
MyProj
├── tsconfig.json
├── custom_typings
│ ├── lib
│ │ ├── index.d.ts
在 tsconfig.json
中配置 typeRoots
:
{
"compilerOptions": {
"typeRoots": ["./custom_typings"]
}
}
這樣,TypeScript 會在 custom_typings
目錄下查找類型定義文件,而不是默認的 node_modules/@types
。
types
types
用於指定 TypeScript 需要包含在編譯中的類型定義包。這個選項允許你精確控制需要包含的類型定義,而不是自動包含 node_modules/@types
下的所有類型定義。
說明範例:
假設你的項目使用了 lodash
和 jquery
,但你只希望包含 lodash
的類型定義。
在 tsconfig.json
中配置 types
:
{
"compilerOptions": {
"types": ["lodash"]
}
}
這樣,TypeScript 只會包含 lodash
的類型定義,而不會包含 jquery
或其他包的類型定義。
Emit
declaration
declaration
選項啟用 TypeScript 編譯器生成 .d.ts
類型定義文件。
說明範例:
假設你的目錄結構如下:
src/
index.ts
utils.ts
tsconfig.json
這樣,編譯後會生成對應的 .d.ts
文件:
dist/
index.js
index.d.ts
utils.js
utils.d.ts
declarationDir
declarationDir
選項用於指定生成的類型定義文件應該放置的目錄。這個選項對於希望將輸出目錄與原始碼目錄分開的項目特別有用。
說明範例:
假設你的項目結構如下:
src/
index.ts
utils.ts
tsconfig.json
這樣,編譯後的類型定義文件會放置在 types
目錄中:
types/
index.d.ts
utils.d.ts
dist/
index.js
utils.js
declarationMap
declarationMap
選項啟用生成 .d.ts.map
文件,這些文件提供了 .d.ts
類型定義文件的來源對應 。這對於偵錯類型定義文件中特別有用,可以追蹤類型資訊到源 TypeScript 文件。
說明範例:
這樣,編譯後會生成對應的 .d.ts.map
文件:
dist/
index.js
index.d.ts
index.d.ts.map
utils.js
utils.d.ts
utils.d.ts.map