Error TypeScript TS1384 termasuk yang terlihat misterius saat pertama kali ditemui, tetapi selalu punya penyebab yang tepat dan bisa diperbaiki. Error ini muncul ketika kompilator menemukan modifier export di tempat yang tidak boleh, tepatnya di dalam blok augmentasi modul. Panduan ini menjelaskan arti TS1384, menelusuri setiap skenario yang memicunya, dan memberi solusi yang tepat untuk masing-masing.
Apa itu TS1384?
Error TypeScript TS1384 membawa pesan: "modifier 'export' tidak dapat diterapkan pada augmentasi modul". Error ini muncul ketika kompilator menemukan kata kunci `export` di dalam blok `declare module` atau `declare global`, dua konstruksi yang dipakai TypeScript untuk augmentasi modul. Blok augmentasi berfungsi memperluas tipe modul yang sudah ada, bukan mendeklarasikan simbol publik baru, jadi `export` secara struktural tidak valid di dalamnya.
Augmentasi modul dalam satu kalimat
Augmentasi modul adalah mekanisme TypeScript yang memungkinkan Anda menambahkan anggota baru ke tipe modul yang sudah ada: misalnya menambahkan properti khusus ke `Express.Request`, memperluas `Window` dengan global pihak ketiga, atau menambahkan metode ke opsi komponen sebuah framework. Sintaksnya mirip blok `declare module 'nama-modul' {}` dan harus berada di file modul (file dengan setidaknya satu pernyataan `import` atau `export` tingkat atas).
- TS1384 adalah error kompilator: file tidak akan lolos pemeriksaan tipe sampai diperbaiki
- Tidak memengaruhi runtime: error ini murni soal deklarasi tipe
- Bersifat deterministik: kode yang sama selalu memicunya, tidak ada versi yang muncul-hilang
- Penyebabnya sedikit: konteks skrip versus modul, isolatedModules, dan struktur .d.ts yang salah mencakup 95% kasus
Note
Kapan TS1384 muncul
Error ini selalu melibatkan `export` di tempat yang tidak diizinkan TypeScript. Ada tiga pola berbeda yang menghasilkannya, dan mengenali pola mana yang berlaku di kode Anda menentukan perbaikan yang tepat.
Pola 1: export di dalam blok declare module
Pemicu paling langsung: Anda menulis pernyataan `export` di dalam augmentasi `declare module`. Niatnya biasanya menambahkan sesuatu ke API publik modul, tetapi blok augmentasi tidak bekerja seperti itu: ia hanya bisa memperluas deklarasi tipe yang sudah ada di modul target.
// ✗ TS1384 - export di dalam augmentasi modul
declare module 'some-library' {
export interface NewInterface { // <-- TS1384 muncul di sini
id: string;
}
}
// ✓ Benar - interface ditambahkan tanpa export
declare module 'some-library' {
interface ExistingInterface {
newProperty: string; // memperluas tipe yang ada
}
}Pola 2: file diperlakukan sebagai skrip, bukan modul
Ini penyebab TS1384 paling umum di proyek nyata. Jika sebuah file tidak punya pernyataan `import` atau `export` tingkat atas, TypeScript memperlakukannya sebagai skrip dengan scope global. Blok `declare global {}` di file skrip tidak bermakna (di skrip semuanya sudah global), sehingga TypeScript menolak setiap `export` di dalamnya dengan TS1384. File tersebut harus berupa modul agar konteks augmentasinya masuk akal.
Pola 3: struktur file .d.ts yang salah
File deklarasi (`.d.ts`) yang mencampur deklarasi modul ambient dengan pernyataan `export` biasa dalam urutan yang salah bisa memicu TS1384. File `.d.ts` yang dimulai dengan deklarasi `export` tingkat atas lalu berisi blok `declare module` diperlakukan sebagai modul, dan itu benar. Tetapi `.d.ts` yang membungkus semuanya dalam satu blok `declare module` lalu mencoba memakai `export` di dalam blok itu mencampur konteks augmentasi dengan konteks definisi modul.
Warning
Cara memperbaiki TS1384: augmentasi modul
Perbaikan yang tepat bergantung pada apa yang sebenarnya ingin Anda capai. Ada dua tujuan berbeda yang bisa mengarah ke TS1384 dan keduanya butuh pendekatan berbeda. Ikuti empat langkah ini untuk menyelesaikan error dengan bersih.
Identifikasi pola mana yang memicu TS1384
Baca pesan kompilator lengkap dengan saksama. Perhatikan ekstensi file (.ts atau .d.ts), nomor baris, dan apakah `export` berada di blok `declare module`, blok `declare global`, atau di tempat lain. Konteks kode di sekitarnya memberi tahu pola mana dari tiga di atas yang berlaku. Jika path error diawali `node_modules/`, langsung ke perbaikan `skipLibCheck`: pola lain tidak berlaku.
Tambahkan export {} untuk mengubah file menjadi modul
Jika file Anda punya blok `declare module` atau `declare global` tetapi tidak punya import atau export tingkat atas, tambahkan `export {}` di bagian atas. Satu baris ini mengubah file dari konteks skrip ke konteks modul, lingkungan yang diwajibkan untuk sintaks augmentasi. Export kosong ini tidak menambah apa pun ke hasil kompilasi: ia murni sinyal konteks untuk TypeScript.
// Tambahkan baris ini agar file menjadi modul
export {};
declare global {
interface Window {
myAnalytics: AnalyticsInstance;
}
}Pindahkan declare global ke modul yang sudah ada
Alternatif dari menambah `export {}` adalah menempatkan blok `declare global` di file yang sudah punya import atau export nyata, seperti titik masuk library, modul fitur, atau file utilitas bersama. Pendekatan ini menjaga augmentasi tipe tetap dekat dengan kode yang diperluasnya, dan itu bisa lebih mudah dipelihara daripada file deklarasi global khusus.
Hapus export dari dalam blok augmentasi
Jika Anda benar-benar ingin menambahkan tipe yang bisa diekspor ke API publik modul yang sudah ada, blok augmentasi bukan tempat yang tepat. Pindahkan deklarasi sepenuhnya ke luar blok `declare module`. Untuk memperluas interface yang sudah ada, gunakan nama interface yang sama tanpa `export` di dalam blok: TypeScript akan menggabungkannya otomatis lewat declaration merging.
Format dan Validator JSON
Validasi tsconfig.json dan package.json Anda untuk error sintaks secara instan di browser, tanpa perlu kompilator TypeScript.
TS1384 dan isolatedModules
Opsi kompilator `isolatedModules` aktif secara default di Vite, Next.js, Create React App dengan Babel, dan proyek apa pun yang memakai esbuild atau SWC. Opsi ini mewajibkan setiap file bisa ditransformasi secara mandiri tanpa informasi tipe antar file, yang menambah batasan dan memperkuat beberapa error TypeScript, termasuk TS1384.
Apa yang dibatasi isolatedModules
| Konstruksi | Tanpa isolatedModules | Dengan isolatedModules |
|---|---|---|
| `const enum` | ✓ Diizinkan di mana saja | ✗ Hanya di file .d.ts |
| `export type` | ✓ Opsional | ✓ Wajib untuk re-export khusus tipe |
| `import type` | ✓ Opsional | ✓ Wajib untuk import khusus tipe |
| Deklarasi ambient | ✓ Di file .ts mana pun | ✓ Lebih baik di file .d.ts |
| Augmentasi modul | ✓ Di file .ts modul | ✓ Sebaiknya di file .d.ts |
| Re-export namespace | ✓ Diizinkan | ✗ Dibatasi |
Perbaikan khusus isolatedModules
Ketika `isolatedModules` aktif dan TS1384 muncul pada augmentasi di file `.ts` biasa, perbaikan terbersihnya adalah memindahkan augmentasi ke file `.d.ts`. File deklarasi tidak pernah ditransformasi oleh esbuild atau Babel: hanya kompilator TypeScript yang membacanya. Ini menghapus batasan isolatedModules sepenuhnya untuk augmentasi tersebut.
// Pola ini bekerja dengan benar bersama isolatedModules
export {};
declare module 'express' {
interface Request {
userId?: string;
tenantId?: string;
}
}Tip
TS1384 di file .d.ts
File deklarasi menambahkan nuansa tersendiri pada TS1384. Aturan tentang apa yang valid di file `.d.ts` sedikit berbeda dari file `.ts` biasa, dan pola yang menghasilkan TS1384 di file deklarasi sering kali kurang intuitif.
Definisi modul ambient atau augmentasi?
File `.d.ts` bisa memuat dua hal yang tampak mirip tetapi berperilaku berbeda. Definisi modul ambient (`declare module 'nama' {}` di `.d.ts` dalam konteks skrip, tanpa import atau export) mendefinisikan seluruh tipe sebuah modul dari nol: ini dipakai untuk memberi tipe pada library JavaScript yang belum bertipe. Augmentasi modul (`declare module 'nama' {}` di `.d.ts` dalam konteks modul dengan `export {}`) memperluas tipe modul yang sudah ada. Perbedaannya penting karena `export` valid di definisi modul ambient, tetapi menghasilkan TS1384 di augmentasi modul.
Memilih pola .d.ts yang tepat
Jika file `.d.ts` Anda mendefinisikan tipe untuk library tanpa tipe (misalnya memberi tipe pada plugin jQuery lama), biarkan sebagai file konteks skrip tanpa `export {}` di awal. Pakai `export` dengan bebas di dalam blok `declare module`. Jika file `.d.ts` Anda memperluas library yang sudah bertipe (misalnya menambahkan properti ke `Express.Request`), tambahkan `export {}` di awal dan hapus setiap `export` dari dalam blok `declare module`.
Note
skipLibCheck sebagai jalan terakhir
Ketika TS1384 muncul pada path di dalam `node_modules` dan Anda tidak mengendalikan paket tersebut, tambahkan `"skipLibCheck": true` ke tsconfig.json. Ini meminta TypeScript melewati pemeriksaan tipe semua file `.d.ts`, termasuk yang ada di `node_modules`. Ini opsi konfigurasi yang sah dan banyak dipakai, bukan trik. Konsekuensinya, Anda kehilangan sepenuhnya pemeriksaan tipe pada deklarasi library, sehingga tipe yang benar-benar rusak di dependensi tidak akan dilaporkan. Pakai `skipLibCheck` hanya bila alternatifnya adalah build Anda tersendat.
TS1384 di framework dan bundler
Setiap framework dan alat build mengonfigurasi TypeScript dengan cara berbeda, jadi TS1384 bisa muncul karena alasan yang sedikit berbeda tergantung stack Anda. Berikut bagaimana lingkungan yang paling umum menghasilkan dan mengatasi error ini.
Next.js
Next.js mengaktifkan `isolatedModules` otomatis lewat tsconfig defaultnya dan memakai SWC untuk transformasi. Pola standar untuk augmentasi tipe di Next.js adalah direktori `types/` khusus di root proyek berisi file `.d.ts` yang dimulai dengan `export {}`. Next.js juga menghasilkan file `next-env.d.ts`: jangan pernah mengeditnya manual, karena file itu dibuat ulang setiap build dan perubahan Anda akan hilang. Letakkan augmentasi Anda di file terpisah.
Vite
Proyek Vite memakai esbuild untuk transformasi dan menyertakan `isolatedModules: true` di tsconfig default. Vite juga menghasilkan file `vite-env.d.ts` untuk global miliknya. Letakkan augmentasi modul di direktori `src/types/` terpisah. Pola `export {}` mengatasi TS1384 di semua pengaturan Vite standar, dan menyimpan augmentasi di file `.d.ts` adalah pendekatan paling aman ketika transformasi esbuild ada di rantai proses.
API Node.js / Express biasa
Proyek Express sering memperluas `Express.Request` untuk menambahkan sesi pengguna atau konteks autentikasi. Pola kanonisnya adalah file `src/types/express/index.d.ts` dengan `export {}` di awal lalu augmentasi `declare module 'express-serve-static-core'`. Tanpa `isolatedModules`, ini juga berfungsi sebagai file `.ts` biasa, tetapi memakai `.d.ts` adalah konvensi yang lebih bersih dalam semua kasus. Selalu verifikasi nama modul yang tepat di definisi tipe Express, karena target augmentasinya adalah `express-serve-static-core`, bukan `express`.
File harus berupa modul sebelum bisa mengaugmentasi modul lain. Satu `export {}` mengubah skrip menjadi modul, dan membuka seluruh sistem augmentasi.
Menghindari TS1384 dalam jangka panjang
TS1384 mudah muncul tanpa sengaja, terutama saat anggota tim baru menambahkan augmentasi tipe atau ketika Anda memigrasi proyek ke bundler baru. Praktik berikut mencegahnya kembali setelah diperbaiki.
Tetapkan konvensi direktori tipe
Buat direktori `src/types/` (atau `types/`) dan simpan semua augmentasi modul di sana sebagai file `.d.ts`. Setiap file sebaiknya memuat satu augmentasi dan dimulai dengan `export {}`. Dokumentasikan konvensi ini di `CONTRIBUTING.md` proyek agar kontributor baru tahu di mana deklarasi tipe diletakkan. Lokasi yang konsisten juga memudahkan audit augmentasi saat memperbarui dependensi.
Gunakan tsc --noEmit di CI
Menjalankan `tsc --noEmit` sebagai langkah CI menangkap TS1384 (dan error TypeScript lainnya) sebelum kode masuk ke main. Banyak proyek melewati pemeriksaan tipe di CI karena bundler tidak mewajibkannya: esbuild dan SWC membuang tipe tanpa memeriksanya. Tambahkan `tsc --noEmit` sebagai langkah terpisah agar error tipe, termasuk TS1384, tertangkap di setiap pull request.
Audit perubahan tsconfig dengan penampil perbedaan
Perubahan pada tsconfig.json, seperti menambahkan `isolatedModules`, mengganti `moduleResolution`, atau memperbarui `lib`, bisa memunculkan TS1384 di file yang sebelumnya terkompilasi bersih. Saat ada perubahan tsconfig, gunakan Penampil perbedaan untuk membandingkan konfigurasi lama dan baru berdampingan. Dengan begitu langsung terlihat opsi mana yang berubah, dan Anda bisa melacak error TS1384 baru ke pengaturan spesifik yang menyebabkannya.
Tip
Penampil perbedaan
Bandingkan dua versi tsconfig.json, package.json, atau file teks apa pun berdampingan untuk melihat tepat apa yang berubah, di browser dan tanpa perlu mengunggah.
Key takeaways
- TS1384 muncul ketika modifier `export` berada di dalam blok augmentasi `declare module` atau `declare global`; perbaikannya hampir selalu satu baris.
- Penyebab paling umum: file tidak punya import atau export, jadi TypeScript memperlakukannya sebagai skrip. Tambahkan `export {}` di awal untuk mengubahnya menjadi modul.
- Dengan `isolatedModules: true` (proyek Vite, Next.js, esbuild), pindahkan augmentasi ke file `.d.ts`: file itu dikecualikan dari transformasi dan sepenuhnya menghindari batasan.
- Jika TS1384 menunjuk ke path di dalam `node_modules`, perbaikannya adalah `skipLibCheck: true` di tsconfig.json: Anda tidak bisa mengedit file deklarasi dependensi.
- Definisi modul ambient (memberi tipe pada library tanpa tipe) dan augmentasi modul (memperluas library bertipe) terlihat mirip tetapi berbeda: `export` di dalam blok hanya valid pada definisi, bukan augmentasi.
- Tambahkan `tsc --noEmit` ke pipeline CI untuk menangkap TS1384 di setiap pull request, meskipun bundler Anda (esbuild, SWC) tidak melakukan pemeriksaan tipe saat build.
- Validasi sintaks tsconfig.json dengan Format dan Validator JSON setelah perubahan: error sintaks JSON diam-diam mencegah TypeScript membaca konfigurasi Anda.