Kuasai TypeScript dari nol. Pelajari cara kerja compiler, setup proyek, jelajahi sistem tipe, dan bangun aplikasi Node.js siap produksi dengan percaya diri.

JavaScript menjalankan miliaran baris kode di seluruh web. Namun, JavaScript bersifat dinamis dalam pengetikan, yang berarti error sering muncul saat runtime—terkadang di produksi. TypeScript mengatasi ini dengan menambahkan pengetikan statis ke JavaScript, menangkap bug saat pengembangan bukan setelah deployment.
Jika Anda pernah menulis JavaScript dan bertanya-tanya mengapa IDE Anda tidak bisa menangkap typo di nama properti, atau mengapa fungsi tiba-tiba menerima undefined padahal Anda mengharapkan string, TypeScript adalah jawabannya.
Panduan ini membimbing Anda melalui segalanya: cara kerja TypeScript di balik layar, setup proyek pertama, memahami sistem tipe, dan membangun aplikasi nyata. Baik Anda developer JavaScript yang ingin meningkatkan skill atau baru dalam pemrograman, Anda akan menemukan pengetahuan praktis dan actionable di sini.
TypeScript adalah superset dari JavaScript yang menambahkan pengetikan statis. Anggap saja sebagai JavaScript dengan pembatas keselamatan—Anda mendeklarasikan tipe apa yang seharusnya dimiliki variabel, fungsi, dan objek Anda, dan compiler TypeScript memeriksa kode Anda sebelum dijalankan.
Inilah insight kunci: TypeScript tidak berjalan langsung di browser atau Node.js. Ia dikompilasi menjadi JavaScript biasa terlebih dahulu. File .ts Anda menjadi file .js, yang kemudian dieksekusi secara normal.
Fleksibilitas JavaScript adalah kekuatan dan kelemahan sekaligus. Anda bisa menulis:
function add(a, b) {
return a + b;
}
add(5, 10); // 15
add("5", "10"); // "510" (penggabungan string, bukan penjumlahan)
add(5, "10"); // "510" (type coercion)Fungsi bekerja, tapi perilakunya tidak dapat diprediksi. TypeScript mencegah ini:
function add(a: number, b: number): number {
return a + b;
}
add(5, 10); // ✓ 15
add("5", "10"); // ✗ Error: Argument of type 'string' is not assignable to parameter of type 'number'Memahami pipeline kompilasi sangat penting. Inilah yang terjadi:
Kode Sumber TypeScript (.ts)
↓
Compiler TypeScript (tsc)
↓
Pengecekan Tipe & Validasi
↓
Output JavaScript (.js)
↓
Runtime JavaScript (Node.js / Browser)Langkah 1: Tulis TypeScript
function greet(name: string): string {
return `Hello, ${name}!`;
}
const message = greet("Alice");
console.log(message);Langkah 2: Kompilasi dengan tsc
tsc hello.tsLangkah 3: JavaScript yang Dihasilkan
function greet(name) {
return `Hello, ${name}!`;
}
const message = greet("Alice");
console.log(message);Perhatikan: Semua anotasi tipe dihapus. Output adalah JavaScript murni.
Langkah 4: Eksekusi
node hello.js
# Output: Hello, Alice!Informasi tipe hanya ada saat pengembangan. Setelah dikompilasi, hilang—runtime tidak tahu atau peduli tentang tipe.
Mari kita bangun proyek Node.js nyata dengan TypeScript.
mkdir my-typescript-app
cd my-typescript-app
npm init -ynpm install --save-dev typescriptnpx tsc --initIni membuat file tsconfig.json dengan default yang masuk akal. Kami akan menyesuaikannya selanjutnya.
Buka tsconfig.json dan perbarui untuk pengembangan Node.js:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Opsi kunci dijelaskan:
target: Versi JavaScript untuk dikompilasi (ES2020 modern dan didukung luas)module: Sistem modul (commonjs untuk Node.js)outDir: Tempat file .js yang dikompilasi disimpanrootDir: Tempat file sumber .ts Anda beradastrict: Aktifkan semua opsi pengecekan tipe ketatsourceMap: Generate file .map untuk debuggingmkdir srcfunction sayHello(name: string): string {
return `Hello, ${name}!`;
}
const greeting = sayHello("World");
console.log(greeting);npx tscPeriksa folder dist—Anda akan melihat hello.js:
node dist/hello.js
# Output: Hello, World!Selamat. Anda telah menulis, mengkompilasi, dan menjalankan TypeScript.
Tipe adalah jantung TypeScript. Mari kita jelajahi tipe inti yang akan Anda gunakan setiap hari.
// String
const name: string = "Alice";
// Number (termasuk integer dan float)
const age: number = 30;
const pi: number = 3.14;
// Boolean
const isActive: boolean = true;
// Undefined dan Null
const nothing: undefined = undefined;
const empty: null = null;
// Symbol (jarang digunakan)
const id: symbol = Symbol("unique-id");
// BigInt (untuk angka sangat besar)
const bigNumber: bigint = 9007199254740991n;// Array string
const colors: string[] = ["red", "green", "blue"];
// Sintaks alternatif
const numbers: Array<number> = [1, 2, 3];
// Array tipe campuran (union type)
const mixed: (string | number)[] = ["hello", 42, "world"];
// Array objek
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];Interface mendefinisikan bentuk objek:
interface Product {
id: number;
name: string;
price: number;
inStock?: boolean; // Properti opsional
}
const laptop: Product = {
id: 1,
name: "MacBook Pro",
price: 1999
// inStock opsional, jadi tidak apa-apa untuk dihilangkan
};
const phone: Product = {
id: 2,
name: "iPhone 15",
price: 999,
inStock: true
};Variabel bisa menjadi salah satu dari beberapa tipe:
type Status = "pending" | "success" | "error";
function handleStatus(status: Status): void {
if (status === "pending") {
console.log("Loading...");
} else if (status === "success") {
console.log("Done!");
} else {
console.log("Something went wrong");
}
}
handleStatus("success"); // ✓
handleStatus("unknown"); // ✗ ErrorGenerics memungkinkan Anda menulis kode yang dapat digunakan kembali untuk tipe apa pun:
// Fungsi generic
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
const firstString = getFirstElement(["a", "b", "c"]); // Tipe: string
const firstNumber = getFirstElement([1, 2, 3]); // Tipe: number
// Interface generic
interface Container<T> {
value: T;
getValue(): T;
}
const stringContainer: Container<string> = {
value: "hello",
getValue() {
return this.value;
}
};Keduanya mendefinisikan tipe, tapi sedikit berbeda:
// Type alias (bisa mewakili tipe apa pun)
type Point = {
x: number;
y: number;
};
// Interface (khusus untuk objek)
interface Point {
x: number;
y: number;
}
// Type alias bisa menjadi union
type ID = string | number;
// Interface tidak bisa menjadi union
// interface ID = string | number; // ✗ Syntax errorUntuk sebagian besar kasus, gunakan interface untuk objek dan type alias untuk yang lain.
Mari kita bangun sesuatu yang praktis: sistem manajemen pengguna sederhana.
src/
├── types/
│ └── user.ts
├── services/
│ └── userService.ts
└── index.tsexport interface User {
id: number;
name: string;
email: string;
age: number;
isActive: boolean;
}
export type CreateUserInput = Omit<User, "id">;
export type UpdateUserInput = Partial<CreateUserInput>;import { User, CreateUserInput, UpdateUserInput } from "../types/user";
class UserService {
private users: User[] = [];
private nextId: number = 1;
createUser(input: CreateUserInput): User {
const user: User = {
id: this.nextId++,
...input
};
this.users.push(user);
return user;
}
getUser(id: number): User | undefined {
return this.users.find(user => user.id === id);
}
getAllUsers(): User[] {
return this.users;
}
updateUser(id: number, input: UpdateUserInput): User | undefined {
const user = this.getUser(id);
if (!user) return undefined;
Object.assign(user, input);
return user;
}
deleteUser(id: number): boolean {
const index = this.users.findIndex(user => user.id === id);
if (index === -1) return false;
this.users.splice(index, 1);
return true;
}
}
export default new UserService();import userService from "./services/userService";
// Buat pengguna
const alice = userService.createUser({
name: "Alice Johnson",
email: "alice@example.com",
age: 28,
isActive: true
});
const bob = userService.createUser({
name: "Bob Smith",
email: "bob@example.com",
age: 35,
isActive: false
});
console.log("Created users:", userService.getAllUsers());
// Update pengguna
userService.updateUser(alice.id, { age: 29 });
console.log("Updated Alice:", userService.getUser(alice.id));
// Hapus pengguna
userService.deleteUser(bob.id);
console.log("After deletion:", userService.getAllUsers());npx tsc
node dist/index.jsOutput:
Created users: [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', age: 28, isActive: true },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', age: 35, isActive: false }
]
Updated Alice: { id: 1, name: 'Alice Johnson', email: 'alice@example.com', age: 29, isActive: true }
After deletion: [ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', age: 29, isActive: true } ]Enums mendefinisikan serangkaian konstanta bernama:
enum UserRole {
Admin = "admin",
Moderator = "moderator",
User = "user"
}
interface Account {
id: number;
role: UserRole;
}
const admin: Account = {
id: 1,
role: UserRole.Admin
};
// Numeric enums (default)
enum Status {
Pending = 0,
Active = 1,
Inactive = 2
}TypeScript menyediakan tipe utility bawaan untuk transformasi umum:
interface User {
id: number;
name: string;
email: string;
}
// Partial: semua properti opsional
type PartialUser = Partial<User>;
// Required: semua properti wajib
type RequiredUser = Required<PartialUser>;
// Readonly: semua properti readonly
type ReadonlyUser = Readonly<User>;
// Pick: pilih properti spesifik
type UserPreview = Pick<User, "id" | "name">;
// Omit: kecualikan properti spesifik
type UserWithoutEmail = Omit<User, "email">;
// Record: buat objek dengan kunci spesifik
type UserRoles = Record<"admin" | "user" | "guest", User>;Tipe yang bergantung pada tipe lain:
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>; // false
// Contoh praktis: ekstrak tipe elemen array
type ArrayElement<T> = T extends (infer E)[] ? E : T;
type StringArray = ArrayElement<string[]>; // string
type SingleNumber = ArrayElement<42>; // 42anyfunction processData(data: any): any {
return data.toUpperCase();
}
// Tidak ada pengecekan tipe—mengalahkan tujuan TypeScriptfunction processData(data: string): string {
return data.toUpperCase();
}
// Aman tipe dan jelasinterface User {
profile?: {
avatar?: string;
};
}
const user: User = {};
const avatar = user.profile.avatar; // ✗ Error: Object is possibly 'undefined'const avatar = user.profile?.avatar; // ✓ Aman, mengembalikan undefined jika profile tidak adafunction printId(id: string | number): void {
console.log(id.toUpperCase()); // ✗ Error: number tidak punya toUpperCase
}function printId(id: string | number): void {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id);
}
}Mode ketat TypeScript menangkap bug nyata. Jangan nonaktifkan:
{
"compilerOptions": {
"strict": false,
"noImplicitAny": false
}
}{
"compilerOptions": {
"strict": true
}
}Selalu aktifkan strict: true di tsconfig.json. Ini menangkap lebih banyak error saat pengembangan.
src/
├── features/
│ ├── users/
│ │ ├── types.ts
│ │ ├── service.ts
│ │ └── controller.ts
│ ├── products/
│ │ ├── types.ts
│ │ ├── service.ts
│ │ └── controller.ts// Baik: Interface yang jelas untuk konsumen eksternal
export interface UserRepository {
findById(id: number): Promise<User | null>;
save(user: User): Promise<void>;
}
// Detail implementasi
class PostgresUserRepository implements UserRepository {
async findById(id: number): Promise<User | null> {
// Implementation
}
async save(user: User): Promise<void> {
// Implementation
}
}TypeScript cerdas dalam menyimpulkan tipe. Jangan over-annotate:
const name: string = "Alice";
const age: number = 30;
const isActive: boolean = true;const name = "Alice"; // disimpulkan sebagai string
const age = 30; // disimpulkan sebagai number
const isActive = true; // disimpulkan sebagai booleanPastikan semua kasus ditangani:
type Status = "pending" | "success" | "error";
function handleStatus(status: Status): string {
switch (status) {
case "pending":
return "Loading...";
case "success":
return "Done!";
case "error":
return "Failed!";
default:
const _exhaustive: never = status; // Error jika kasus hilang
return _exhaustive;
}
}/**
* Mewakili respons paginated dari API.
* @template T Tipe item dalam respons
*/
interface PaginatedResponse<T> {
/** Array item untuk halaman ini */
items: T[];
/** Total jumlah item di semua halaman */
total: number;
/** Nomor halaman saat ini (1-indexed) */
page: number;
/** Jumlah item per halaman */
pageSize: number;
}TypeScript tidak selalu pilihan yang tepat:
Untuk script sekali pakai atau prototype cepat, overhead TypeScript tidak sebanding:
# Cukup gunakan Node.js langsung
node script.jsSaat menjelajahi ide dengan cepat, fleksibilitas JavaScript bisa lebih cepat.
Jika tim Anda tidak nyaman dengan TypeScript, kurva pembelajaran mungkin memperlambat awalnya.
Jika proyek Anda memiliki sedikit dependensi eksternal, keuntungan keamanan tipe lebih kecil.
Kompilasi ulang otomatis saat file berubah:
npx tsc --watch{
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js",
"dev:run": "tsc && node dist/index.js"
}
}Kemudian jalankan:
npm run dev # Watch mode
npm run build # Kompilasi sekali
npm start # Jalankan kode yang dikompilasiSource maps memungkinkan debugging TypeScript langsung:
{
"compilerOptions": {
"sourceMap": true,
"outDir": "./dist"
}
}Di VS Code, buat .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/dist/index.js",
"preLaunchTask": "tsc: build",
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}TypeScript mengubah cara Anda menulis JavaScript. Dengan menangkap error saat pengembangan, menyediakan autocomplete IDE, dan membuat kode self-documenting, ini menghemat waktu dan mencegah bug.
Poin-poin kunci:
Langkah selanjutnya:
TypeScript bukan hanya tentang tipe—ini tentang kepercayaan diri. Tulis sekali, tangkap bug lebih awal, dan deploy dengan ketenangan pikiran.