Modules & Declaration — TypeScript

Import/Export types: // types.ts export interface User { nama: string; umur: number; } export type ID = string | number; // app.ts import type { User, ID }…

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-rupiah yang gak punya type sama sekali, lalu menulis import { format } from "format-rupiah". TypeScript diam saja, atau protes? Tebak dulu, jawabannya ada di bagian "Library tanpa type: .d.ts dan declare 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]

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
}

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

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.

Yang akan kamu pelajari