Setiap kali compiler menemukan pernyataan import, ia mencari berkas yang dirujuk specifier-nya sebelum tipe berkas itu dapat diperiksa. Topik ini membahas dua mode resolusi modul yang paling sering dipakai, yaitu nodenext yang meniru aturan Node.js dan bundler yang meniru perilaku alat bundler, beserta opsi paths untuk membuat alias jalur. Pembahasan memakai tiga proyek kecil dengan konfigurasi berbeda, sehingga perbedaan perilakunya dapat dibandingkan secara langsung. Setelah membacanya, mode resolusi dapat dipilih sesuai lingkungan eksekusi program, dan alias dapat ditulis sambil mengetahui batasannya.
Dua mode resolusi modul#
Opsi moduleResolution menentukan aturan yang dipakai compiler untuk menerjemahkan specifier menjadi berkas di disk. Mode node16 dan nodenext meniru perilaku Node.js modern, termasuk aturan yang mewajibkan ekstensi berkas pada pernyataan impor ESM, sedangkan mode bundler meniru alat penggabung berkas yang menyelesaikan ekstensi itu sendiri. Opsi module mengikuti mode yang dipilih, misalnya "module": "nodenext" yang otomatis memakai resolusi nodenext. Perbedaan paling terlihat antara keduanya dirangkum pada tabel berikut.
| Perilaku | nodenext | bundler |
|---|---|---|
| Specifier relatif tanpa ekstensi pada impor ESM | ditolak | diterima |
Specifier berakhiran .ts | dengan allowImportingTsExtensions | dengan allowImportingTsExtensions |
| Lingkungan yang ditiru | Node.js | alat bundel seperti esbuild dan Vite |
Pilihan mode sebaiknya mengikuti alat yang benar-benar menjalankan program, bukan soal selera. Program yang dieksekusi Node.js secara langsung memakai mode nodenext, sedangkan aplikasi yang selalu melewati esbuild atau Vite memakai mode bundler. Mode yang keliru membuat pemeriksaan tipe menyetujui kode yang justru gagal saat dijalankan, atau sebaliknya menolak kode yang sebenarnya sah.
Ekstensi wajib pada mode nodenext#
Proyek proyek-node/ menyimulasikan proyek Node.js modern. Berkas package.json menyatakan "type": "module", sehingga berkas .ts di dalamnya diperlakukan sebagai modul ESM, dan tsconfig.json memakai mode nodenext. Berkas satuan.ts mengekspor satu fungsi konversi suhu, sedangkan laporan-salah.ts mengimpornya tanpa menulis ekstensi.
{
"name": "proyek-node",
"private": true,
"type": "module"
}{
"compilerOptions": {
"strict": true,
"noEmit": true,
"module": "nodenext",
"moduleResolution": "nodenext",
"target": "es2022",
"allowImportingTsExtensions": true
}
}export function celsiusKeFahrenheit(celsius: number): number {
return (celsius * 9) / 5 + 32;
}// tidak-dijalankan
import { celsiusKeFahrenheit } from "./satuan";
console.log("25 C =", celsiusKeFahrenheit(25), "F");# memeriksa proyek dengan mode nodenext
tsc --project proyek-node --pretty falseproyek-node/laporan-salah.ts(2,37): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './satuan.js'?Pesan TS2835 menyatakan aturan mode nodenext secara harfiah, yaitu specifier relatif pada impor ESM wajib mencantumkan ekstensi berkasnya. Satu-satunya pesan menunjuk laporan-salah.ts, sedangkan berkas lain lolos tanpa keluhan. Node.js menuntut ekstensi itu karena modul ESM diselesaikan satu per satu saat berjalan, tanpa mekanisme menebak ekstensi seperti pada sistem modul lama.
Versi yang benar hanya menuliskan ekstensi secara lengkap. Berkas laporan.ts memakai specifier ./satuan.ts, dan pemeriksaan yang sama tidak melaporkan apa pun tentangnya, sehingga program langsung dapat dijalankan.
// tidak-dijalankan
import { celsiusKeFahrenheit } from "./satuan.ts";
console.log("25 C =", celsiusKeFahrenheit(25), "F");# menjalankan versi yang benar dari dalam folder proyeknya
( cd proyek-node && node laporan.ts )25 C = 77 FMode bundler menerima specifier tanpa ekstensi#
Proyek proyek-bundler/ memakai specifier tanpa ekstensi yang sama dengan laporan-salah.ts sebelumnya, tetapi konfigurasinya kini memilih mode bundler. Berkas satuan.ts di dalamnya menyalin isi berkas pada proyek sebelumnya, sedangkan laporan.ts mengimpornya tanpa ekstensi. Pemeriksaan compiler terhadap proyek ini berakhir tanpa satu pun pesan.
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"target": "es2022"
}
}// tidak-dijalankan
import { celsiusKeFahrenheit } from "./satuan";
console.log("25 C =", celsiusKeFahrenheit(25), "F");# memeriksa proyek dengan mode bundler
tsc --project proyek-bundler --pretty falseMode bundler berarti compiler meniru cara esbuild menyelesaikan specifier, sehingga kesesuaian antara pemeriksaan tipe dan alat build terjaga. Kesepakatan itu bermakna hanya bila program benar-benar melewati bundler, karena Node.js sendiri tidak menyelesaikan specifier tanpa ekstensi. Perintah berikut membundle kedua berkas dengan esbuild lalu menjalankan hasilnya, sehingga jalur eksekusi yang diasumsikan konfigurasi ini terbukti benar-benar dapat dijalankan.
# membundle proyek di folder sementara lalu menjalankan hasilnya
rm -rf /tmp/rm-bundler && mkdir -p /tmp/rm-bundler
cp proyek-bundler/laporan.ts proyek-bundler/satuan.ts /tmp/rm-bundler/
( cd /tmp/rm-bundler && esbuild laporan.ts --bundle --outfile=bundle.js --log-level=silent && node bundle.js )25 C = 77 FAlias jalur dengan paths#
Opsi paths memetakan awalan specifier ke jalur di dalam proyek, sehingga kode dapat mengimpor dengan nama yang pendek tanpa rangkaian ../ yang panjang. Proyek proyek-alias/ memetakan @satuan/* ke folder src/, sehingga @satuan/satuan menunjuk berkas src/satuan.ts yang isinya sama dengan proyek-proyek sebelumnya. Pemetaan ini hanya dikenali compiler, dan pemeriksaan terhadap proyek ini berakhir tanpa pesan.
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"target": "es2022",
"paths": {
"@satuan/*": ["./src/*"]
}
}
}// tidak-dijalankan
import { celsiusKeFahrenheit } from "@satuan/satuan";
console.log("25 C =", celsiusKeFahrenheit(25), "F");# memeriksa proyek dengan pemetaan paths
tsc --project proyek-alias --pretty false# menjalankan berkas yang mengimpor alias tanpa dukungan runtime
( cd proyek-alias && node src/laporan.ts 2>&1 | grep -m1 "Cannot find package" )Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@satuan/satuan' imported from /home/kal/my-project/site/content/typescript/modul-dan-perkakas/resolusi-modul/proyek-alias/src/laporan.tsPemeriksaan tipe lolos karena compiler membaca paths, sedangkan eksekusi langsung gagal karena Node.js tidak membaca opsi tersebut. Pesan error tersebut memperlakukan @satuan/satuan sebagai nama package yang tidak ditemukan, bukan sebagai berkas yang salah tulis. Agar program benar-benar berjalan, pemetaan yang sama harus dinyatakan pada alat yang menjalankannya, misalnya opsi alias milik esbuild atau resolve.alias milik Vite.
Kesalahan umum#
Kesalahan berikut sering muncul saat memilih mode resolusi dan menulis alias.
- Memakai mode
bundleruntuk program yang dijalankan Node.js secara langsung. Pemeriksaan tipe lolos, tetapi eksekusi gagal mencari berkasnya karena specifier tanpa ekstensi tidak pernah diselesaikan oleh Node.js. - Menganggap
pathsberlaku pada semua alat. Opsi ini hanya dibaca compiler, sehingga pemetaan yang sama harus ditulis ulang di konfigurasi bundler, runner pengujian, maupun alat lain yang memproses specifier. - Menulis nilai pemetaan
pathstanpa awalan./, misalnya"src/*". TypeScript 7 menolaknya dengan pesan TS5090, yaitu nilai paths yang tidak relatif tidak diizinkan, sehingga awalan./wajib ditulis. - Melupakan
"type": "module"padapackage.jsonproyek Node.js. Tanpa deklarasi itu, berkas.tsdianggap CommonJS, sehingga aturan ekstensi khas ESM yang dituntut modenodenexttidak sepenuhnya berlaku.
Ringkasan#
- Opsi
moduleResolutionmenentukan aturan penerjemahan specifier menjadi berkas, dengannodenextyang meniru Node.js danbundleryang meniru alat penggabung berkas. - Mode
nodenextmewajibkan ekstensi pada specifier relatif impor ESM melalui pesan TS2835, sedangkan modebundlermenerima specifier tanpa ekstensi. - Mode
bundlerbermakna hanya bila program benar-benar melewati bundler, karena Node.js tidak menyelesaikan ekstensi untuk siapa pun. - Opsi
pathsmembuat alias yang hanya dikenali compiler, sehingga pemetaan yang sama wajib dinyatakan pada alat yang menjalankan program agar eksekusinya berhasil.
Referensi#
- moduleResolution — TSConfig Reference
— Daftar strategi resolusi modul beserta perilaku mode
node16,nodenext, danbundler. - paths — TSConfig Reference — Penjelasan pemetaan alias dan aturan penulisannya.
- Modules Reference — TypeScript Handbook — Rujukan perilaku modul, termasuk aturan ekstensi specifier pada Node.js.