Di TypeScript, tiap file adalah module yang berbagi isinya lewat export dan import, sama seperti module JavaScript. TypeScript menambah dua hal: import yang cuma membawa type, dan file .d.ts yang isinya cuma type. File .d.ts itu mirip buku menu restoran: menu memberi tahu ada hidangan apa dan bahannya, tapi menu bukan dapurnya. TypeScript membaca menunya, JavaScript yang memasak.
🧪 Tebakan cepat: kamu memasang library JavaScript
format-rupiahyang gak punya type sama sekali, lalu menulisimport { format } from "format-rupiah". TypeScript diam saja, atau protes? Tebak dulu, jawabannya ada di bagian "Library tanpa type:.d.tsdandeclare module".
export dan import
export membuat sesuatu di sebuah file bisa dipakai file lain. Tanpa export, isinya cuma bisa dipakai di file itu sendiri. Di TypeScript, type dan interface juga bisa di-export. Contoh dengan tiga file:
// src/types.ts
export interface User {
nama: string
umur: number
}
// src/pair.ts
export function createPair<A, B>(a: A, b: B): [A, B] {
return [a, b]
}
// src/app.ts
import { createPair } from "./pair"
import type { User } from "./types"
const budi: User = { nama: "Budi", umur: 17 }
const pasangan = createPair(budi.nama, budi.umur) // pasangan: [string, number]
console.log(pasangan) // ["Budi", 17]
export function createPairbisa diambilapp.tsdenganimport { createPair } from "./pair". Nama di dalam kurung kurawal harus sama dengan nama yang di-export.createPair<A, B>punya dua type parameter. TypeScript mengisinya dari argumen:createPair("hello", 42)mengembalikan["hello", 42]bertipe[string, number], dancreatePair(true, "yes")mengembalikan[true, "yes"]bertipe[boolean, string].[A, B]adalah tuple: array dengan dua isi, urutannya tetap.
import type: hilang waktu compile
User cuma type, jadi gak ada yang perlu di-import waktu kode jalan. import type bilang begitu terus terang. Ini app.js hasil compile-nya:
import { createPair } from "./pair"
const budi = { nama: "Budi", umur: 17 }
const pasangan = createPair(budi.nama, budi.umur)
console.log(pasangan)
Baris import type hilang total, dan types.ts jadi types.js yang isinya cuma export {}. Template project Vite menyalakan opsi verbatimModuleSyntax, yang membuat TypeScript membiarkan semua import yang gak ditandai type apa adanya. Jadi import { User } from "./types" ditolak:
error TS1484: 'User' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
Kalau dibiarkan, baris itu ikut masuk ke JS, padahal types.js gak punya apa pun bernama User, jadi app.js gagal dimuat.
Library tanpa type: .d.ts dan declare module
format-rupiah di contoh ini adalah library JavaScript biasa. Isinya index.js saja:
// node_modules/format-rupiah/index.js
exports.format = function (angka) {
return "Rp " + angka.toLocaleString("id-ID")
}
TypeScript gak bisa menebak tipe dari kode JavaScript di node_modules, jadi import-nya ditolak:
error TS7016: Could not find a declaration file for module 'format-rupiah'. '/app/node_modules/format-rupiah/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/format-rupiah` if it exists or add a new declaration (.d.ts) file containing `declare module 'format-rupiah';`
Itu jawaban tebakannya: TypeScript protes dengan TS7016. Obatnya file .d.ts yang kamu tulis sendiri, misalnya src/format-rupiah.d.ts:
declare module "format-rupiah" {
export function format(angka: number): string
}
declareartinya "ini sudah ada di tempat lain, aku cuma menjelaskan tipenya". Gak ada badan fungsi, gak adareturn.declare module "format-rupiah"menempelkan penjelasan itu ke nama package-nya.- File
.d.tsgak menghasilkan JS sama sekali. Taruh di dalam folder yang dibaca tsconfig, misalnyasrc.
Sekarang TypeScript tahu format menerima number:
Type untuk format-rupiah |
format(15000) |
format("15000") |
|---|---|---|
| belum ada | error TS7016 | error TS7016 |
.d.ts lengkap di atas |
OK, mencetak Rp 15.000 |
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. |
versi singkat declare module "format-rupiah"; |
OK, mencetak Rp 15.000 |
gak ada error, mencetak Rp 15000 |
Versi singkat menghilangkan TS7016, tapi semua isinya jadi any. Teks "15000" lolos, dan hasilnya salah diam-diam: titik ribuannya hilang.
@types: .d.ts buatan komunitas
Untuk library populer, biasanya sudah ada yang menulis .d.ts-nya. Proyek DefinitelyTyped menerbitkannya sebagai package @types/nama-library:
npm install --save-dev @types/lodash
TypeScript otomatis mencari di node_modules/@types, jadi gak ada yang perlu di-import. Banyak library modern (axios, zod) bahkan sudah membawa .d.ts sendiri, jadi langsung jalan. Urutannya: cek apakah library-nya sudah punya type, lalu cari @types/..., baru tulis declare module sendiri.
⚠️ Jebakan yang sering ketemu
- Lupa
export. Fungsi tanpaexportgak bisa di-import dari file lain:error TS2459: Module '"./pair"' declares 'createPair' locally, but it is not exported. importbiasa untuk type di project Vite. DenganverbatimModuleSyntax, hasilnya error TS1484. Pakaiimport type.- Menulis isi fungsi di
.d.ts. File deklarasi cuma berisi tanda tangan: nama, parameter dan tipe kembalian. Kodenya tetap di file JS-nya. - Meng-import file lain di latihan. Latihannya cuma punya satu file.
exportjalan, dan pengecekannya membacacreatePairlewat export-nya, tapiimport { x } from "./lain"gak bisa: pemeriksanya menjawabCannot find module './lain'.
TL;DR: export membuat isi file bisa dipakai file lain, dan import { nama } from "./file" mengambilnya. import type membawa type saja dan hilang total dari JS; di project dengan verbatimModuleSyntax, import type tanpa type jadi error TS1484. File .d.ts berisi type tanpa isi dan gak menghasilkan JS. Library JS tanpa type memicu TS7016: pasang @types/nama-library dari DefinitelyTyped kalau ada, atau tulis declare module "nama-library" { ... } sendiri. Versi singkat declare module "nama-library"; membuat semuanya any.