Menu

tsconfig.json w TypeScript: opcje, domyślne i przykłady

tsconfig.json oznacza folder jako projekt TypeScript i ustawia opcje kompilatora. Opcje, które mają znaczenie (target, module, moduleResolution, strict, rootDir, outDir, include, lib, types, noEmit, skipLibCheck), zalecana konfiguracja startowa, extends i zmiany w TypeScript 7.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

tsconfig.json to plik konfiguracyjny projektu TypeScript. Gdy uruchamiasz tsc bez argumentów, kompilator szuka go w bieżącym folderze (a potem w folderach nadrzędnych), odczytuje opcje z compilerOptions i sprawdza pliki wskazane przez include. Mały, ale kompletny przykład:

{
    "compilerOptions": {
        "target": "es2022",
        "module": "nodenext",
        "strict": true,
        "rootDir": "./src",
        "outDir": "./dist"
    },
    "include": ["src"]
}

To kompiluje każdy plik TypeScript z src do JavaScriptu w dist jako kod ES2022, z regułami modułów Node.js i wszystkimi sprawdzeniami strict. npx tsc --init generuje dłuższy plik startowy z komentarzem przy każdej opcji.

Plik to JSON z komentarzami: akceptowane są komentarze //, komentarze /* */ i przecinki na końcu.

Zalecana konfiguracja startowa

Dla aplikacji albo skryptu w 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"]
}

Do typów Node.js potrzebne jest npm install --save-dev @types/node. Przy module: "nodenext" pole "type" w package.json decyduje, czy plik .ts jest modułem ES ("type": "module"), czy CommonJS ("type": "commonjs", które zapisuje npm init -y). W nowych projektach, które używają import i export, ustaw "type": "module".

Dla kodu budowanego przez bundler, taki jak Vite, esbuild albo webpack, JavaScript zapisuje bundler, a tsc tylko sprawdza:

{
    "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"]
}

Startery frameworków (Vite, Next.js, Angular) generują własny tsconfig.json. Zacznij od ich wersji, zamiast ją zastępować.

Opcje, które mają znaczenie

OpcjaCo robiDomyślnie w TypeScript 7
targetWersja JavaScriptu na wyjściu, nowsza składnia jest przepisywana dla starszych targetówes2025
libTypy wbudowanych API znane modułowi sprawdzania (Array.prototype.at, Map, document)Zgodne z target, plus DOM
moduleRodzaj emitowanego kodu modułów i stosowane reguły modułówesnext
moduleResolutionJak ścieżki importów są znajdowane na dyskunodenext albo node16 przy pasującym module, w przeciwnym razie bundler
strictWłącza całą rodzinę sprawdzeń stricttrue
rootDirFolder, którego struktura jest odwzorowywana w outDirFolder, w którym leży tsconfig.json
outDirGdzie trafiają pliki .js (i .d.ts)Obok każdego pliku źródłowego
include / exclude / filesKtóre pliki należą do projektuKażdy plik .ts w folderze
typesKtóre pakiety @types ładują się bez importu[], żadne
noEmitTylko sprawdzanie, nic nie jest zapisywanefalse
declarationZapisuje też pliki deklaracji typów .d.tsfalse
sourceMapZapisuje pliki .js.map dla debuggerówfalse
esModuleInteropPozwala, by import x from "cjs-package" działało z pakietami CommonJStrue (nie da się wyłączyć)
skipLibCheckPomija sprawdzanie typów w plikach .d.ts, także w node_modulesfalse

target i lib

target mówi, na jakiej wersji JavaScriptu ma działać wyjście. Składnia nowsza niż target jest przepisywana: przy "target": "es2017" pole klasy albo ??= zamienia się w starszy kod. Najniższy target, jaki akceptuje TypeScript 7, to es2015 (es6), a es5 usunięto.

target nie dodaje brakujących API w czasie wykonania. Opisanie ich to zadanie lib, a dostarczenie to zadanie polyfilla. lib mówi modułowi sprawdzania, które wbudowane obiekty i metody istnieją. Jego wartość domyślna wynika z target i obejmuje też typy DOM przeglądarki, więc document przechodzi sprawdzanie typów nawet w projekcie Node.js, dopóki sam nie ustawisz lib. Użycie metody z nowszego standardu niż twoje lib to błąd kompilacji:

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.

Uruchamialne przykłady w tej dokumentacji używają lib es2022, więc toSorted, Object.groupBy i nowe metody Set nie są w nich dostępne.

module i moduleResolution

module ma trzy sensowne wartości dla nowego kodu:

  • nodenext: dla kodu uruchamianego przez Node.js. Każdy plik jest ESM albo CommonJS zależnie od rozszerzenia (.mts, .cts) albo pola "type" w najbliższym package.json, dokładnie tak, jak decyduje Node. Względne importy w modułach ES wymagają rozszerzenia pliku, zapisanego jako .js, mimo że źródło to .ts: import { add } from "./math.js". moduleResolution dopasowuje się automatycznie.
  • preserve: dla kodu przetwarzanego przez bundler. Importy zostają tak, jak je zapisano, a rozwiązywanie korzysta z reguł bundler, które pozwalają na importy bez rozszerzenia.
  • esnext: zwykłe wyjście w modułach ES z rozwiązywaniem bundler. To wartość domyślna, gdy module nie jest ustawione.

Częsty pierwszy błąd przy konfiguracji z tsc --init bierze się stąd, że npm init -y zapisuje "type": "commonjs":

src/main.ts(1,10): error TS1295: ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.

Plik używa import, ale package.json mówi, że projekt jest w CommonJS. Zmień "type" na "module". Importy i eksporty szczegółowo omawia strona o modułach.

strict i opcje sprawdzania

"strict": true włącza naraz grupę sprawdzeń: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, strictBuiltinIteratorReturn, noImplicitThis i useUnknownInCatchVariables. To ustawienie domyślne w TypeScript 7, włączone w każdym uruchamialnym przykładzie tutaj. Kod pisany pod tryb strict zawęża wartość, która może nie istnieć, zanim jej użyje:

Bez adnotacji typu parametru nie da się wywnioskować, a noImplicitAny to zgłasza:

index.ts(2,17): error TS7006: Parameter 'n' implicitly has an 'any' type.

strict nie obejmuje każdego przydatnego sprawdzenia. Warto dodać dwa: noUncheckedIndexedAccess (element tablicy albo odczyt przez index signature ma typ T | undefined) i exactOptionalPropertyTypes (właściwości opcjonalnej nie da się jawnie ustawić na undefined). tsc --init włącza oba. Sprawdzenia stylu, takie jak noUnusedLocals, noImplicitReturns i noFallthroughCasesInSwitch, są domyślnie wyłączone.

Które pliki: include, exclude, rootDir, outDir

Bez include albo files projekt zawiera każdy plik .ts, .tsx i .d.ts z folderu i jego podfolderów. node_modules jest zawsze pomijany. outDir też jest pomijany, ale tylko dopóki nie ustawisz exclude: własna lista exclude zastępuje to ustawienie domyślne, więc dopisz do niej folder wyjściowy. include i exclude przyjmują wzorce glob:

{
    "include": ["src", "scripts/**/*.ts"],
    "exclude": ["src/**/*.test.ts"]
}

exclude tylko filtruje to, co znalazło include. Plik importowany przez inny włączony plik i tak jest kompilowany.

outDir to miejsce na wyjście, a rootDir to część drzewa źródeł, która jest w nim odwzorowywana: przy "rootDir": "./src" plik src/api/users.ts staje się dist/api/users.js. Ustaw oba. rootDir domyślnie wskazuje folder, w którym leży tsconfig.json, więc jeśli ustawisz tylko outDir, a źródła leżą w src, TypeScript 7 zatrzyma się z komunikatem:

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.

Opcje wyjścia: noEmit, declaration, sourceMap

  • "noEmit": true sprawia, że tsc tylko sprawdza typy. Używaj tego, gdy JavaScript tworzy inne narzędzie (bundler, tsx, usuwanie typów w Node.js).
  • "declaration": true zapisuje plik .d.ts dla każdego modułu, czyli typy bez kodu. Biblioteki go potrzebują, żeby ich użytkownicy dostali typy. declarationMap dodaje mapy, dzięki którym "go to definition" prowadzi do źródła .ts.
  • "sourceMap": true zapisuje pliki .js.map, żeby debuggery i stack trace wskazywały linie w .ts.
  • "noEmitOnError": true nie zapisuje niczego, dopóki są błędy typów. Bez tego tsc zgłasza błędy i mimo to zapisuje JavaScript.

types, esModuleInterop i skipLibCheck

types wymienia pakiety @types, które ładują się globalnie, bez importu. W TypeScript 7 domyślnie jest to pusta lista, więc po npm install --save-dev @types/node dopisujesz też "types": ["node"]. Inaczej process i require pozostają nieznane (error TS2591: Cannot find name 'process'). Test runnery z funkcjami globalnymi, takimi jak describe z Jest, trafiają na tę samą listę.

esModuleInterop sprawia, że działają domyślne importy pakietów CommonJS (import express from "express"). W TypeScript 7 jest zawsze włączone, a ustawienie go na false to błąd.

skipLibCheck pomija sprawdzanie typów w plikach deklaracji. Przyspiesza budowanie i pozwala uniknąć błędów w node_modules, których i tak nie naprawisz, kosztem tego, że nie zauważysz konfliktów między typami dwóch bibliotek. Większość projektów to włącza.

Współdzielenie ustawień przez extends

extends wczytuje inną konfigurację i pozwala temu plikowi nadpisać jej części:

{
    "extends": "./tsconfig.base.json",
    "compilerOptions": {
        "rootDir": "./src",
        "outDir": "./dist"
    },
    "include": ["src"]
}

compilerOptions są łączone opcja po opcji, a include, exclude i files z bazowego pliku są zastępowane, a nie łączone, jeśli ten plik je ustawia. Ścieżki w pliku bazowym są rozwiązywane względem pliku bazowego. extends przyjmuje też pakiet: projekt @tsconfig/bases publikuje po jednym na każde środowisko, na przykład npm install --save-dev @tsconfig/node24, a potem "extends": "@tsconfig/node24/tsconfig.json".

Żeby zobaczyć ostateczne ustawienia po zastosowaniu wszystkich extends i wartości domyślnych, uruchom:

npx tsc --showConfig

Opcje usunięte w TypeScript 7

TypeScript 6 oznaczył te ustawienia jako przestarzałe, a TypeScript 7 je usunął. Konfiguracja, która nadal używa któregoś z nich, kończy się błędem error TS5108 albo TS5102, na przykład Option 'baseUrl' has been removed. Please remove it from your configuration.

Usunięte ustawienieUżyj zamiast tego
"target": "es5"es2015 albo nowszy
"moduleResolution": "node" (node10) albo "classic"nodenext albo bundler
"module": "amd", "umd", "system", "none"nodenext, esnext albo preserve, a dla innych formatów bundler
baseUrlWpisy paths względne wobec pliku tsconfig
outFileBundler
downlevelIterationNic: targety od es2015 wzwyż obsługują iterację natywnie
"esModuleInterop": false, "allowSyntheticDefaultImports": false, "alwaysStrict": falseUsuń tę linię, te opcje są zawsze włączone

Strona o TypeScript 7 wymienia pozostałe zmiany w tym wydaniu, w tym nowe wartości domyślne, których nie obejmuje ta tabela.

Najczęściej zadawane pytania

Czym jest tsconfig.json?

To plik konfiguracyjny projektu TypeScript. Jego obecność oznacza folder jako katalog główny projektu, compilerOptions ustawia, jak kompilator sprawdza i emituje kod, a include, exclude albo files mówią, które pliki należą do projektu. Uruchomienie tsc bez argumentów go odczytuje.

Jak utworzyć plik tsconfig.json?

Uruchom npx tsc --init w folderze projektu (z zainstalowanym TypeScriptem). Polecenie zapisuje tsconfig.json z zalecanymi ustawieniami i komentarzem przy każdej opcji. Możesz też napisać plik ręcznie: {} to poprawny tsconfig, który używa wszystkich wartości domyślnych.

Na co ustawić target w tsconfig?

Na najstarszą wersję JavaScriptu, na której ma działać twój kod. Dla aktualnego Node.js bezpieczne jest es2022 albo nowsze, a domyślna wartość w TypeScript 7 to es2025. target decyduje, która nowsza składnia jest przepisywana dla starszych silników, i wybiera też domyślne lib, czyli zestaw wbudowanych API znanych modułowi sprawdzania typów.

Czym różni się module od moduleResolution?

module decyduje, jaki rodzaj kodu modułów zapisuje kompilator (ES import/export albo CommonJS require) i które reguły modułów obowiązują. moduleResolution decyduje, jak na dysku znajdowana jest ścieżka importu, taka jak "./utils.js" albo "lodash". Używaj nodenext dla kodu uruchamianego przez Node.js, a module: "preserve" (co oznacza rozwiązywanie bundler) dla kodu przetwarzanego przez bundler.

Czy tsconfig.json może mieć komentarze?

Tak. Kompilator czyta go jako JSON z komentarzami: dozwolone są komentarze // i /* */ oraz przecinki na końcu, dlatego tsc --init zapisuje plik pełen zakomentowanych opcji. Inne narzędzia, które parsują go jako ścisły JSON, mogą się na nich wywrócić.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ