Menerbitkan Tipe

Field types pada package.json dan pemaketan berkas deklarasi.

Daftar isi

Pustaka TypeScript tidak diterbitkan dalam bentuk berkas .ts-nya, melainkan sebagai JavaScript yang dijalankan oleh runtime dan berkas deklarasi .d.ts yang dibaca compiler serta editor. Topik ini membahas pembangkitan deklarasi lewat opsi declaration, field main dan types pada package.json, pemetaan exports dengan conditions, hingga field files yang membatasi isi paket. Setelah membacanya, sebuah pustaka kecil dapat dipaketkan lengkap dengan tipenya.

Deklarasi hasil kompilasi#

Pustaka mini berikut berisi satu modul konversi suhu yang diekspor lewat sintaks ES modules. Kompilasinya memakai opsi declaration yang menghasilkan satu berkas .d.ts untuk setiap berkas sumber, ditambah rootDir agar tata letak folder sumber tetap terkendali dan outDir untuk menampung hasilnya. Compiler yang dipakai jalur ini bahkan menuntut rootDir dinyatakan secara eksplisit pada tata letak seperti ini, dan tanpanya kompilasi berhenti dengan pesan TS5011 sebelum satu berkas pun dihasilkan.

json proyek-konversi/tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "declaration": true,
    "rootDir": "src",
    "module": "commonjs",
    "target": "es2022",
    "outDir": "dist"
  }
}
ts proyek-konversi/src/konversi.ts
export interface Suhu {
  nilai: number;
  satuan: "C" | "F";
}

export function keCelsius(suhu: Suhu): number {
  if (suhu.satuan === "F") {
    return ((suhu.nilai - 32) * 5) / 9;
  }
  return suhu.nilai;
}

export function keFahrenheit(suhu: Suhu): number {
  if (suhu.satuan === "C") {
    return (suhu.nilai * 9) / 5 + 32;
  }
  return suhu.nilai;
}
bash
# mengompilasi pustaka dengan opsi declaration
tsc --project proyek-konversi/tsconfig.json
# melihat isi folder hasil kompilasi
ls proyek-konversi/dist
# membaca berkas deklarasi yang dihasilkan
cat proyek-konversi/dist/konversi.d.ts
text Keluaran
konversi.d.ts
konversi.js
export interface Suhu {
    nilai: number;
    satuan: "C" | "F";
}
export declare function keCelsius(suhu: Suhu): number;
export declare function keFahrenheit(suhu: Suhu): number;

Berkas konversi.d.ts memuat seluruh tanda tangan publik modul, termasuk interface Suhu dan union literal "C" | "F" yang tetap utuh. Isinya deklarasi tanpa implementasi, sehingga editor dapat menampilkan tipe tanpa membaca isi fungsi. Perhatikan bahwa fungsi dinyatakan lewat declare function yang hanya membawa tanda tangan, tanpa tubuh fungsi sama sekali. Berkas konversi.js di sampingnya adalah kode yang benar-benar dijalankan, dan keduanya berpasangan satu banding satu.

bash
# mengonsumsi hasil kompilasi lewat require biasa
node -e "const { keFahrenheit, keCelsius } = require('./proyek-konversi/dist/konversi.js'); console.log(keFahrenheit({ nilai: 100, satuan: 'C' })); console.log(keCelsius({ nilai: 212, satuan: 'F' }));"
text Keluaran
212
100

Hasil konversinya sesuai hitungan manual, yaitu 100 derajat Celsius setara 212 Fahrenheit, dan 212 derajat Fahrenheit setara 100 Celsius. Bagian runtime inilah yang sampai ke pengguna pustaka, sedangkan tipenya dibawa oleh berkas deklarasi. Keduanya dihasilkan oleh satu perintah tsc yang sama.

Field main dan types pada package.json#

package.json adalah kontrak paket, dan dua field-nya menentukan tempat runtime serta compiler harus mencari pintu masuknya. Field main menunjuk berkas JavaScript yang dijalankan saat paket diimpor, sedangkan field types menunjuk berkas deklarasi yang dibaca TypeScript. Nilai keduanya ditulis relatif terhadap akar paket, dan pada tata letak ini bersebelahan di folder dist. Bagi konsumen, keduanya bekerja pada lapisan berbeda, yaitu bundler dan runtime mengikuti main, sedangkan editor dan tsc mengikuti types saat menampilkan saran dan memeriksa kode.

json proyek-konversi/package.json
{
  "name": "konversi-suhu",
  "version": "1.0.0",
  "description": "Konversi satuan suhu Celsius dan Fahrenheit",
  "main": "dist/konversi.js",
  "types": "dist/konversi.d.ts",
  "files": [
    "dist"
  ]
}

Tanpa field types, TypeScript masih mencari berkas .d.ts di samping berkas main, sehingga konversi.d.ts tetap ditemukan pada tata letak ini. Field types tetap ditulis karena menyatakan niat secara eksplisit dan tetap dibaca oleh alat yang tidak mencari berkas .d.ts di samping main secara otomatis. Nama typings adalah alias lama yang berperilaku sama, dan cukup salah satu yang dipakai. Pasangan main dan types hanya mampu menyatakan satu pintu masuk, sehingga paket dengan lebih dari satu titik impor memerlukan pemetaan exports pada bagian berikutnya.

exports dengan condition types#

Field exports memetakan jalur impor ke berkas di dalam paket, dan conditions pada tiap entrinya diperiksa dari atas ke bawah. Condition types harus ditulis lebih dahulu agar alat TypeScript menemukan deklarasi sebelum condition default mengambil alih, sedangkan default menjadi jawaban untuk runtime dan alat lain. Pola ini membuat satu entri exports mampu melayani editor, bundler, dan runtime sekaligus tanpa menduplikasi konfigurasi.

json Contoh: exports dengan condition types
{
  "exports": {
    ".": {
      "types": "./dist/konversi.d.ts",
      "default": "./dist/konversi.js"
    }
  }
}

Begitu exports ada, pemetaan ini diutamakan oleh Node.js modern, bundler, dan TypeScript pada mode resolusi node16 maupun bundler, sementara main dan types tetap berguna untuk alat lama. Kunci "." menunjuk pintu masuk utama paket, dan paket yang lebih besar dapat menambahkan subjalur seperti "./format" di sampingnya tanpa mengubah pintu utama. Urutan condition juga berlaku umum, sehingga condition khusus seperti import atau require dapat disisipkan di antara types dan default bila runtime yang berbeda perlu dibedakan. Dengan pemetaan ini, jalur berkas internal tidak lagi dijanjikan secara publik, sehingga struktur folder dapat diubah tanpa merusak proyek konsumen yang mengikuti pintu resminya.

Daftar files dan pemeriksaan tarball#

Field files adalah daftar putih isi paket, yaitu hanya jalur yang disebutkan yang dikirim ke registry. Daftar dist membatasi paket pada hasil kompilasi, sehingga berkas sumber dan konfigurasi tidak ikut diterbitkan. Isi paket dapat diperiksa tanpa jaringan maupun akun lewat npm pack --dry-run, yang menampilkan isi tarball, yaitu arsip .tgz hasil pemaketan, tanpa benar-benar menerbitkannya.

bash
# memeriksa isi tarball tanpa menerbitkan apa pun
( cd proyek-konversi && npm pack --dry-run )
text Keluaran
npm notice
npm notice 📦  [email protected]
npm notice Tarball Contents
npm notice 181B dist/konversi.d.ts
npm notice 412B dist/konversi.js
npm notice 208B package.json
npm notice Tarball Details
npm notice name: konversi-suhu
npm notice version: 1.0.0
npm notice filename: konversi-suhu-1.0.0.tgz
npm notice package size: 509 B
npm notice unpacked size: 801 B
npm notice shasum: 6d2633ba2ec98a196ef4749197232299b0ac2220
npm notice integrity: sha512-4ZZcXz7IVdFI1[...]GwQUTQesHRKbQ==
npm notice total files: 3
npm notice
konversi-suhu-1.0.0.tgz

Daftar isinya tiga berkas, yaitu dua hasil kompilasi dan package.json. Meskipun files hanya menyebut dist, npm selalu menyertakan package.json beserta berkas metadata seperti README bila ada. Kedua hasil kompilasi terbawa karena dist disebut, sedangkan src dan tsconfig.json tidak muncul. Perintah penerbitan sesungguhnya, npm publish, membutuhkan akun registry dan koneksi jaringan sehingga tidak dijalankan di sini, dan pemeriksaan tarball di atas adalah langkah terakhir sebelum perintah itu dilakukan.

Kesalahan umum#

Kesalahan pertama adalah menerbitkan tanpa deklarasi. Paket yang hanya membawa berkas .js dipakai tanpa tipe, sehingga konsumen TypeScript kehilangan seluruh pemeriksaan terhadap API pustaka. Opsi declaration pada tsconfig mencegahnya karena deklarasi ikut dihasilkan di folder yang sama dengan kodenya.

Kesalahan kedua adalah menunjuk types ke jalur yang tidak ada. Field yang salah tulis membuat pemuatan deklarasi gagal diam-diam pada sebagian alat, sehingga jalurnya perlu diperiksa terhadap isi tarball, bukan hanya terhadap folder kerja. Kesalahan ketiga adalah membiarkan files kosong, karena tanpa daftar putih npm menyertakan seluruh folder paket saat menerbitkan, termasuk berkas sumber, pengujian, dan berkas sisa, sehingga ukuran paket membengkak dan isi internal ikut terbaca. Kesalahan keempat adalah menguji paket hanya dari folder kerjanya sendiri, karena seluruh berkas paket memang tersedia di tempat itu. Pengujian yang lebih akurat dilakukan terhadap isi tarball, sebagaimana dilakukan npm pack --dry-run di atas, atau dengan memasang paket pada proyek contoh yang bersih.

Ringkasan#

  • Opsi declaration menghasilkan pasangan .js dan .d.ts untuk setiap berkas sumber, dengan rootDir dan outDir menjaga tata letak hasil.
  • Field main menunjuk titik masuk runtime dan types menunjuk titik masuk deklarasi, sedangkan typings adalah aliasnya.
  • Pemetaan exports diutamakan oleh resolusi modern, dan condition types wajib ditulis sebelum default agar deklarasi tetap ditemukan.
  • Field files menjadi daftar putih isi paket, dan npm pack --dry-run memperlihatkan isi tarball tanpa jaringan maupun akun.

Referensi#