Web Development

TypeScript Dasar: JavaScript dengan Tipe Data

Panduan lengkap belajar TypeScript dari nol β€” tipe data, interfaces, generics, enums, utility types, hingga migrasi dari JavaScript

1. Pengenalan TypeScript

TypeScript adalah bahasa pemrograman open-source yang dikembangkan oleh Microsoft, dibangun di atas JavaScript dengan tambahan sistem tipe statis. TypeScript dikompilasi menjadi JavaScript biasa sehingga bisa berjalan di mana saja β€” browser, Node.js, Deno, atau Bun.

TypeScript pertama kali dirilis pada tahun 2012 oleh Anders Hejlsberg, arsitek utama bahasa C# dan pencipta Turbo Pascal. Sejak saat itu, TypeScript telah menjadi salah satu bahasa paling populer di dunia pengembangan web, terutama untuk proyek skala besar.

Mengapa Menggunakan TypeScript?

Keunggulan Penjelasan
Tipe StatisMendeteksi error saat development, bukan saat runtime di production
AutocompletionIDE memberikan saran kode yang lebih akurat dan lengkap
Refactoring AmanMengubah nama fungsi/variabel dengan percaya diri β€” TypeScript akan menunjukkan semua yang perlu diubah
Dokumentasi HidupTipe data berfungsi sebagai dokumentasi yang selalu ter-update
Kompatibel 100%Semua kode JavaScript valid di TypeScript β€” migrasi bertahap
Ekosistem BesarDefinisi tipe tersedia untuk hampir semua library populer (@types/*)
Standar IndustriFramework besar seperti Angular, Vue 3, dan Next.js menggunakan TypeScript

TypeScript vs JavaScript

Aspek TypeScript JavaScript
Tipe DataStatis (compile-time)Dinamis (runtime)
Error DetectionSaat kompilasiSaat eksekusi
Interfacesβœ… Ya❌ Tidak ada
Genericsβœ… Ya❌ Tidak ada
Enumsβœ… Ya❌ Tidak ada
KompilasiPerlu di-compile ke JSLangsung dieksekusi
Kurva BelajarSedikit lebih curamLebih mudah untuk pemula

Instalasi TypeScript

Bash
# Instal TypeScript secara global
npm install -g typescript

# Cek versi
tsc --version
# Output: Version 5.x.x

# Inisialisasi proyek TypeScript
mkdir my-ts-project && cd my-ts-project
npm init -y
tsc --init  # Membuat file tsconfig.json

# Compile file TypeScript
tsc index.ts    # Menghasilkan index.js

# Jalankan langsung dengan ts-node (opsional)
npx ts-node index.ts
Diagram: Alur Kerja TypeScript
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ALUR KERJA TYPESCRIPT                         β”‚
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  .ts files   β”‚    β”‚   Compiler   β”‚    β”‚  .js files       β”‚  β”‚
β”‚  β”‚  (kode TS    │───►│   (tsc)      │───►│  (kode JS        β”‚  β”‚
β”‚  β”‚   + tipe)    β”‚    β”‚              β”‚    β”‚   yang valid)    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                             β”‚                                   β”‚
β”‚                             β–Ό                                   β”‚
β”‚                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                      β”‚ tsconfig.jsonβ”‚  ← Konfigurasi compiler   β”‚
β”‚                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                                                 β”‚
β”‚  Error ditemukan DI SINI ──► Bukan di production!               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Tipe Data Dasar

TypeScript menyediakan berbagai tipe data bawaan untuk mendefinisikan struktur data Anda secara eksplisit. Deklarasi tipe menggunakan sintaks : TipeData setelah nama variabel.

Tipe Primitif

TypeScript β€” Tipe Primitif
// ===== TIPE PRIMITIF =====

// String
let nama: string = "BeebaneLabs";
let salam: string = `Halo, ${nama}!`;  // Template literal juga bekerja

// Number (integer dan float menggunakan tipe yang sama)
let umur: number = 25;
let harga: number = 99.99;
let hex: number = 0xff;         // Hexadecimal
let binary: number = 0b1010;    // Binary

// Boolean
let isActive: boolean = true;
let isLoggedIn: boolean = false;

// Null dan Undefined
let tidakAda: null = null;
let belumDidefinisikan: undefined = undefined;

// Any β€” menonaktifkan pengecekan tipe (HINDARI penggunaan!)
let apapun: any = "string";
apapun = 42;         // OK
apapun = true;       // OK β€” TIDAK ada error!

// Unknown β€” alternatif yang lebih aman dari any
let tidakDiketahui: unknown = "mungkin string";
// tidakDiketahui.toUpperCase();  // ERROR! Harus di-check dulu
if (typeof tidakDiketahui === "string") {
    tidakDiketahui.toUpperCase();  // OK β€” type narrowing
}

// Void β€” untuk fungsi yang tidak mengembalikan nilai
function sapa(name: string): void {
    console.log(`Halo, ${name}!`);
    // Tidak ada return statement
}

// Never β€” untuk fungsi yang tidak pernah selesai
function throwError(msg: string): never {
    throw new Error(msg);
    // Kode setelah throw tidak pernah dijalankan
}

Array dan Tuple

TypeScript β€” Array & Tuple
// ===== ARRAY =====
let angka: number[] = [1, 2, 3, 4, 5];
let buah: Array<string> = ["apel", "mangga", "jeruk"]; // Generic syntax

// Array campuran β€” gunakan union type
let campuran: (string | number)[] = [1, "dua", 3, "empat"];

// Readonly array β€” tidak bisa diubah
let readonlyArr: readonly number[] = [10, 20, 30];
// readonlyArr.push(40);  // ERROR! Method push tidak tersedia


// ===== TUPLE =====
// Tuple: array dengan jumlah elemen dan tipe yang tetap
let koordinat: [number, number] = [-6.2088, 106.8456]; // Jakarta
let pengguna: [string, number, boolean] = ["Budi", 28, true];

// Tuple dengan label (labeled tuple) β€” untuk dokumentasi
type HttpResponse = [statusCode: number, message: string, data: object];
let response: HttpResponse = [200, "OK", { users: [] }];

// Tuple dengan optional element
type FlexTuple = [string, number, boolean?];
let a: FlexTuple = ["hello", 42];       // OK β€” boolean opsional
let b: FlexTuple = ["hello", 42, true]; // OK

// Destructuring tuple
let [lat, lng] = koordinat;
console.log(`Latitude: ${lat}, Longitude: ${lng}`);
πŸ’‘ Kapan Menggunakan Any?

Hindari penggunaan any sebisa mungkin. Gunakan unknown sebagai gantinya karena lebih aman β€” Anda wajib melakukan type checking sebelum menggunakan nilainya. Gunakan any hanya saat migrasi bertahap dari JavaScript atau saat berinteraksi dengan library lama yang belum ada definisi tipenya.

3. Interfaces & Type Aliases

Interface dan Type Alias adalah dua cara untuk mendefinisikan struktur data kustom di TypeScript. Keduanya sering bisa digunakan secara bergantian, tetapi memiliki perbedaan penting.

Interface Dasar

TypeScript β€” Interface
// ===== INTERFACE =====
interface User {
    id: number;
    name: string;
    email: string;
    age?: number;           // Opsional (boleh tidak ada)
    readonly createdAt: Date; // Readonly β€” tidak bisa diubah setelah dibuat
}

// Menggunakan interface
const user1: User = {
    id: 1,
    name: "Budi Santoso",
    email: "budi@beebane.com",
    createdAt: new Date()
};

// user1.id = 99;  // OK β€” bisa diubah
// user1.createdAt = new Date();  // ERROR! Readonly property
// user1.phone = "081234";  // ERROR! Property tidak didefinisikan


// ===== EXTENDS (PEWARISAN) =====
interface Admin extends User {
    role: "admin" | "superadmin";  // Literal type
    permissions: string[];
}

const admin1: Admin = {
    id: 2,
    name: "Admin Utama",
    email: "admin@beebane.com",
    createdAt: new Date(),
    role: "superadmin",
    permissions: ["read", "write", "delete", "manage_users"]
};


// ===== INTERFACE MERGING =====
// Interface dengan nama yang sama otomatis di-merge
interface Config {
    host: string;
    port: number;
}

interface Config {
    debug: boolean;  // Ditambahkan ke interface Config yang sudah ada
}

// Sekarang Config punya 3 property: host, port, debug
const cfg: Config = {
    host: "localhost",
    port: 3000,
    debug: true
};

Type Alias

TypeScript β€” Type Alias
// ===== TYPE ALIAS =====
type ID = string | number;  // Union type sebagai alias

type Product = {
    id: ID;
    name: string;
    price: number;
    category: Category;
};

type Category = "elektronik" | "pakaian" | "makanan" | "minuman";

const laptop: Product = {
    id: "PRD-001",
    name: "Laptop ASUS ROG",
    price: 15000000,
    category: "elektronik"
};

// Type alias bisa untuk primitive, union, tuple, dll
type Centimeters = number;
type Point = [x: number, y: number];
type Callback = (data: string) => void;

// Intersection type (menggabungkan beberapa tipe)
type Timestamped = {
    createdAt: Date;
    updatedAt: Date;
};

type UserWithTimestamp = User & Timestamped;
// Sekarang UserWithTimestamp punya semua property dari User + Timestamped

Interface vs Type Alias

Fitur Interface Type Alias
Extends/Inheritanceβœ… extendsβœ… Intersection (&)
Declaration Mergingβœ… Otomatis merge❌ Error jika duplikat
Union Type❌ Tidak bisaβœ… Tipe1 | Tipe2
Primitive Alias❌ Tidak bisaβœ… type ID = string
Tuple❌ Tidak bisaβœ… type Pair = [a, b]
Computed Properties❌ Tidak bisaβœ… mapped types
RekomendasiUntuk object shapes & API contractsUntuk union, utility, & tipe kompleks

4. Generics

Generics memungkinkan Anda menulis kode yang fleksibel dan reusable tanpa mengorbankan type safety. Generics menggunakan parameter tipe (biasanya <T>) yang bisa diisi dengan tipe spesifik saat digunakan.

TypeScript β€” Generics Dasar
// ===== GENERICS DASAR =====

// Tanpa generics β€” harus buat fungsi terpisah untuk tiap tipe
function getFirstNumber(arr: number[]): number {
    return arr[0];
}
function getFirstString(arr: string[]): string {
    return arr[0];
}

// Dengan generics β€” SATU fungsi untuk semua tipe!
function getFirst<T>(arr: T[]): T {
    return arr[0];
}

const angka = getFirst<number>([10, 20, 30]);      // Tipe: number
const teks = getFirst<string>(["a", "b", "c"]);   // Tipe: string
// TypeScript otomatis infer tipe juga:
const auto = getFirst([true, false]);               // Tipe: boolean


// ===== GENERICS DENGAN CONSTRAINT =====
interface HasLength {
    length: number;
}

function logLength<T extends HasLength>(item: T): void {
    console.log(`Panjang: ${item.length}`);
}

logLength("hello");        // OK β€” string punya .length
logLength([1, 2, 3]);      // OK β€” array punya .length
logLength({ length: 10 }); // OK β€” object punya .length
// logLength(123);          // ERROR! number tidak punya .length


// ===== GENERICS DENGAN INTERFACE =====
interface ApiResponse<T> {
    status: number;
    message: string;
    data: T;
    timestamp: Date;
}

interface Product {
    id: number;
    name: string;
    price: number;
}

interface User {
    id: number;
    name: string;
    email: string;
}

// Menggunakan generic interface dengan tipe berbeda
const productResponse: ApiResponse<Product> = {
    status: 200,
    message: "OK",
    data: { id: 1, name: "Laptop", price: 10000000 },
    timestamp: new Date()
};

const userResponse: ApiResponse<User[]> = {
    status: 200,
    message: "OK",
    data: [
        { id: 1, name: "Budi", email: "budi@test.com" },
        { id: 2, name: "Ani", email: "ani@test.com" }
    ],
    timestamp: new Date()
};

Multiple Type Parameters

TypeScript β€” Generics Lanjutan
// ===== MULTIPLE GENERICS =====

// Fungsi dengan dua type parameter
function merge<T, U>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

const merged = merge(
    { name: "Budi" },
    { age: 25 }
);
// Tipe: { name: string } & { age: number }
console.log(merged.name); // "Budi"
console.log(merged.age);  // 25


// ===== GENERIC CLASS =====
class DataStore<T> {
    private items: T[] = [];

    add(item: T): void {
        this.items.push(item);
    }

    getById(index: number): T | undefined {
        return this.items[index];
    }

    getAll(): T[] {
        return [...this.items];
    }

    remove(index: number): T | undefined {
        return this.items.splice(index, 1)[0];
    }
}

// Gunakan dengan tipe spesifik
const userStore = new DataStore<User>();
userStore.add({ id: 1, name: "Budi", email: "budi@test.com" });
const user = userStore.getById(0);
// user otomatis bertipe User | undefined

const numberStore = new DataStore<number>();
numberStore.add(100);
numberStore.add(200);
console.log(numberStore.getAll()); // [100, 200]


// ===== DEFAULT GENERIC =====
interface PaginatedResponse<T = any> {
    data: T[];
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
}

// Tanpa parameter β€” default ke any
const genericResp: PaginatedResponse = {
    data: [{ id: 1 }],
    total: 100,
    page: 1,
    pageSize: 10,
    totalPages: 10
};

// Dengan parameter β€” tipe spesifik
const userResp: PaginatedResponse<User> = {
    data: [{ id: 1, name: "Budi", email: "budi@test.com" }],
    total: 50,
    page: 1,
    pageSize: 10,
    totalPages: 5
};
πŸ“– Kapan Menggunakan Generics?

Gunakan generics saat Anda ingin membuat fungsi, class, atau interface yang bisa bekerja dengan berbagai tipe data tetapi tetap mempertahankan type safety. Contoh nyata: API response wrapper, data store, event handler, dan utility functions.

5. Enums

Enum (enumeration) adalah cara untuk mendefinisikan sekumpulan konstan yang bermakna. TypeScript mendukung numeric enums, string enums, dan heterogenous enums.

TypeScript β€” Enums
// ===== NUMERIC ENUM =====
// Secara default, nilai dimulai dari 0
enum Direction {
    Up,       // 0
    Down,     // 1
    Left,     // 2
    Right     // 3
}

// Atur nilai awal secara manual
enum HttpStatus {
    OK = 200,
    Created = 201,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404,
    ServerError = 500
}

function handleResponse(status: HttpStatus): void {
    switch (status) {
        case HttpStatus.OK:
            console.log("Berhasil!");
            break;
        case HttpStatus.NotFound:
            console.log("Tidak ditemukan!");
            break;
        case HttpStatus.ServerError:
            console.log("Server error!");
            break;
    }
}

handleResponse(HttpStatus.OK);     // "Berhasil!"
handleResponse(HttpStatus.NotFound); // "Tidak ditemukan!"


// ===== STRING ENUM =====
// Setiap nilai harus diisi secara eksplisit
enum UserRole {
    Admin = "ADMIN",
    Editor = "EDITOR",
    Viewer = "VIEWER",
    Guest = "GUEST"
}

enum PaymentMethod {
    CreditCard = "CREDIT_CARD",
    BankTransfer = "BANK_TRANSFER",
    EWallet = "E_WALLET",
    Cash = "CASH"
}

// Keunggulan string enum: lebih mudah dibaca saat debugging
console.log(UserRole.Admin);   // "ADMIN" (bukan 0)
console.log(PaymentMethod.EWallet); // "E_WALLET" (bukan 2)


// ===== CONST ENUM =====
// Di-inline saat compile β€” tidak menghasilkan object JS
const enum Color {
    Red = "#FF0000",
    Green = "#00FF00",
    Blue = "#0000FF"
}

const favoriteColor: Color = Color.Blue;
// Compile menjadi: const favoriteColor = "#0000FF";
// Lebih kecil ukuran output, tapi tidak bisa di-iterate


// ===== ENUM SEBAGAI FLAG (Bitwise) =====
enum Permission {
    None    = 0,      // 0000
    Read    = 1,      // 0001
    Write   = 2,      // 0010
    Execute = 4,      // 0100
    Delete  = 8       // 1000
}

// Gabungkan permission dengan bitwise OR
let userPerm: Permission = Permission.Read | Permission.Write;
// userPerm = 3 (0011)

// Cek permission dengan bitwise AND
const canRead = (userPerm & Permission.Read) !== 0;     // true
const canDelete = (userPerm & Permission.Delete) !== 0;  // false
⚠️ Enum vs Union Type

Banyak developer TypeScript senior lebih memilih string literal union type (type Dir = "up" | "down" | "left" | "right") daripada enum karena lebih ringan saat compile dan tidak menghasilkan object JS tambahan. Gunakan enum untuk kasus yang membutuhkan runtime mapping atau bitwise operations.

6. Union Types & Type Assertion

Union type memungkinkan variabel memiliki beberapa tipe sekaligus. Digunakan dengan operator | (pipe). Type assertion memberi tahu compiler bahwa Anda lebih tahu tipe suatu nilai.

TypeScript β€” Union Types
// ===== UNION TYPES =====

// Variabel bisa bertipe string ATAU number
let id: string | number;
id = "USR-001";  // OK
id = 42;          // OK
// id = true;     // ERROR! boolean bukan bagian dari union

// Fungsi yang menerima beberapa tipe
function formatId(id: string | number): string {
    if (typeof id === "string") {
        return id.toUpperCase();  // TypeScript tahu di sini id = string
    }
    return `ID-${id.toString().padStart(5, "0")}`; // Di sini id = number
}

console.log(formatId("abc"));      // "ABC"
console.log(formatId(42));          // "ID-00042"


// ===== LITERAL TYPES =====
// Tipe yang hanya bisa berisi nilai tertentu
type Theme = "light" | "dark" | "auto";
type StatusCode = 200 | 201 | 400 | 404 | 500;

function setTheme(theme: Theme): void {
    document.documentElement.setAttribute("data-theme", theme);
}

setTheme("dark");   // OK
// setTheme("blue"); // ERROR! "blue" bukan bagian dari union


// ===== DISCRIMINATED UNIONS =====
// Pola yang sangat powerful untuk menangani berbagai bentuk data
interface Circle {
    kind: "circle";     // Discriminant
    radius: number;
}

interface Rectangle {
    kind: "rectangle";  // Discriminant
    width: number;
    height: number;
}

interface Triangle {
    kind: "triangle";   // Discriminant
    base: number;
    height: number;
}

type Shape = Circle | Rectangle | Triangle;

function hitungLuas(shape: Shape): number {
    switch (shape.kind) {
        case "circle":
            return Math.PI * shape.radius ** 2;
        case "rectangle":
            return shape.width * shape.height;
        case "triangle":
            return 0.5 * shape.base * shape.height;
    }
}

// Exhaustiveness check β€” memastikan semua case ditangani
const lingkaran: Circle = { kind: "circle", radius: 10 };
console.log(`Luas lingkaran: ${hitungLuas(lingkaran).toFixed(2)}`);
// Luas lingkaran: 314.16


// ===== TYPE ASSERTION =====
// Memberi tahu compiler tentang tipe yang lebih spesifik

// Sintaks "as"
let input: unknown = "Hello TypeScript";
let panjang: number = (input as string).length;  // OK

// Sintaks angle bracket (tidak bisa di JSX/TSX)
let panjang2: number = (<string>input).length;  // OK di .ts, bukan .tsx

// Non-null assertion (!) β€” menghapus undefined dan null dari tipe
function findUser(id: number): User | undefined {
    return id === 1
        ? { id: 1, name: "Budi", email: "budi@test.com", createdAt: new Date() }
        : undefined;
}

const user = findUser(1);
// user?.name;           // OK β€” optional chaining
const userName = user!.name; // Non-null assertion β€” Anda YAKIN user tidak undefined

7. Utility Types

TypeScript menyediakan berbagai utility types bawaan untuk memanipulasi tipe β€” membuat tipe baru dari tipe yang sudah ada tanpa perlu menulis ulang definisi lengkap.

TypeScript β€” Utility Types
// Base interface untuk contoh-contoh berikut
interface Product {
    id: number;
    name: string;
    price: number;
    description: string;
    category: string;
    inStock: boolean;
}


// ===== Partial<T> β€” Semua property jadi opsional =====
type PartialProduct = Partial<Product>;
// { id?: number; name?: string; price?: number; ... }

function updateProduct(id: number, updates: Partial<Product>): void {
    // updates hanya berisi field yang ingin diubah
    console.log(`Update product ${id}:`, updates);
}
updateProduct(1, { price: 150000 }); // OK β€” hanya update price


// ===== Required<T> β€” Semua property jadi wajib =====
type RequiredProduct = Required<Product>;


// ===== Readonly<T> β€” Semua property jadi readonly =====
type ReadonlyProduct = Readonly<Product>;


// ===== Pick<T, K> β€” Pilih beberapa property saja =====
type ProductSummary = Pick<Product, "id" | "name" | "price">;
// { id: number; name: string; price: number; }

const summary: ProductSummary = {
    id: 1,
    name: "Laptop",
    price: 10000000
};


// ===== Omit<T, K> β€” Hapus beberapa property =====
type ProductWithoutPrice = Omit<Product, "price" | "description">;
// { id: number; name: string; category: string; inStock: boolean; }


// ===== Record<K, V> β€” Membuat tipe object dengan key dan value =====
type UserRoles = Record<string, string[]>;

const roles: UserRoles = {
    admin: ["read", "write", "delete"],
    editor: ["read", "write"],
    viewer: ["read"]
};

// Lebih spesifik dengan literal types
type ProductMap = Record<number, Product>;
const products: ProductMap = {
    1: { id: 1, name: "Laptop", price: 10000000, description: "...", category: "elektronik", inStock: true },
    2: { id: 2, name: "Mouse", price: 150000, description: "...", category: "elektronik", inStock: false }
};


// ===== Exclude<T, U> & Extract<T, U> =====
type AllTypes = string | number | boolean | null | undefined;
type NonNullableTypes = Exclude<AllTypes, null | undefined>;
// string | number | boolean

type OnlyStringNumber = Extract<AllTypes, string | number>;
// string | number


// ===== ReturnType<T> β€” Ambil tipe return dari fungsi =====
function createUser() {
    return {
        id: 1,
        name: "Budi",
        email: "budi@test.com",
        createdAt: new Date()
    };
}

type NewUser = ReturnType<typeof createUser>;
// { id: number; name: string; email: string; createdAt: Date }


// ===== Parameters<T> β€” Ambil tipe parameter dari fungsi =====
function greet(name: string, greeting: string): string {
    return `${greeting}, ${name}!`;
}

type GreetParams = Parameters<typeof greet>;
// [name: string, greeting: string]
πŸ’‘ Utility Types Custom

Anda juga bisa membuat utility types sendiri! Misalnya type Nullable<T> = T | null atau type DeepReadonly<T> yang membuat semua property nested menjadi readonly. Kombinasikan dengan mapped types dan conditional types untuk manipulasi tipe yang lebih powerful.

8. Konfigurasi tsconfig.json

File tsconfig.json adalah konfigurasi utama TypeScript compiler. Di sini Anda mengatur target JavaScript output, strict mode, module system, dan berbagai opsi lainnya.

JSON β€” tsconfig.json Rekomendasi
{
  "compilerOptions": {
    // ===== TARGET & MODULE =====
    "target": "ES2022",           // Target versi JavaScript output
    "module": "ESNext",           // Module system (ESNext, CommonJS, AMD)
    "moduleResolution": "bundler", // Cara resolve import (bundler, node)
    "lib": ["ES2022", "DOM", "DOM.Iterable"],

    // ===== STRICT MODE (SANGAT DIREKOMENDASIKAN!) =====
    "strict": true,               // Aktifkan SEMUA strict checks
    // Atau secara manual:
    // "noImplicitAny": true,     // Error jika tipe any tersembunyi
    // "strictNullChecks": true,  // null/undefined harus eksplisit
    // "strictFunctionTypes": true,
    // "strictBindCallApply": true,
    // "strictPropertyInitialization": true,

    // ===== OUTPUT =====
    "outDir": "./dist",           // Folder output JavaScript
    "rootDir": "./src",           // Folder sumber TypeScript
    "sourceMap": true,            // Generate source map untuk debugging
    "declaration": true,          // Generate .d.ts declaration files
    "declarationMap": true,       // Source map untuk declaration files

    // ===== ADDITIONAL CHECKS =====
    "noUnusedLocals": true,       // Error jika ada variabel yang tidak dipakai
    "noUnusedParameters": true,   // Error jika ada parameter yang tidak dipakai
    "noImplicitReturns": true,    // Error jika fungsi tidak return di semua path
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,

    // ===== INTEROP =====
    "esModuleInterop": true,      // Kompatibilitas CommonJS/ES Module
    "allowSyntheticDefaultImports": true,
    "isolatedModules": true,      // Kompatibel dengan bundler (Vite, esbuild)
    "resolveJsonModule": true,    // Bisa import file .json
    "skipLibCheck": true,         // Skip pengecekan file .d.ts

    // ===== PATH ALIASES =====
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"]
    }
  },
  "include": ["src/**/*"],        // File yang di-compile
  "exclude": ["node_modules", "dist"] // File yang diabaikan
}

Opsi Penting yang Perlu Diketahui

Opsi Default Penjelasan
strictfalseAktifkan semua strict checks β€” WAJIB true untuk proyek baru
targetES3Target JS output β€” gunakan ES2020+ untuk fitur modern
moduleCommonJSModule system β€” gunakan ESNext untuk Vite/modern bundler
outDir./Folder output JS β€” pisahkan dari source
sourceMapfalseGenerate source map β€” aktifkan untuk debugging
esModuleInteropfalseKompatibilitas import β€” aktifkan untuk menghindari masalah
noUnusedLocalsfalseError jika ada variabel tidak dipakai β€” bantu menjaga kebersihan kode

9. Migrasi dari JavaScript

TypeScript dirancang untuk mendukung migrasi bertahap dari JavaScript. Anda tidak perlu mengubah semua file sekaligus β€” mulai dari file yang paling kritis dan tambahkan tipe secara bertahap.

Langkah 1: Pasang TypeScript

Bash β€” Setup TypeScript di Proyek JS
# Di proyek JavaScript yang sudah ada
npm install --save-dev typescript @types/node

# Buat tsconfig.json
npx tsc --init

# Ubah pengaturan agar JS dan TS bisa berdampingan:
# "allowJs": true,         ← Izinkan file .js
# "checkJs": true,         ← Cek tipe di file .js juga (opsional)
# "outDir": "./dist",
# "strict": false          ← Mulai dengan false, aktifkan bertahap

Langkah 2: Rename Bertahap

Strategi Migrasi Bertahap
proyek-saya/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   β”œβ”€β”€ helpers.js        ← Tahap 1: Biarkan .js
β”‚   β”‚   β”œβ”€β”€ validators.js     ← Tahap 2: Rename jadi .ts
β”‚   β”‚   └── format.ts         ← Tahap 3: Sudah .ts dengan tipe lengkap
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ Button.jsx        ← React: rename ke .tsx
β”‚   β”‚   └── Modal.tsx         ← Sudah TypeScript
β”‚   └── app.ts                ← Entry point sudah TypeScript
β”œβ”€β”€ tsconfig.json
└── package.json

Langkah 3: Pola Migrasi Umum

TypeScript β€” Pola Migrasi
// ===== SEBELUM (JavaScript) =====
// utils.js
function formatCurrency(amount, currency) {
    return new Intl.NumberFormat('id-ID', {
        style: 'currency',
        currency: currency || 'IDR'
    }).format(amount);
}

function calculateDiscount(price, discountPercent) {
    return price - (price * discountPercent / 100);
}

module.exports = { formatCurrency, calculateDiscount };


// ===== SESUDAH (TypeScript) =====
// utils.ts
type Currency = "IDR" | "USD" | "EUR" | "JPY";

interface FormatOptions {
    currency?: Currency;
    locale?: string;
}

function formatCurrency(
    amount: number,
    options: FormatOptions = {}
): string {
    const { currency = "IDR", locale = "id-ID" } = options;
    return new Intl.NumberFormat(locale, {
        style: "currency",
        currency
    }).format(amount);
}

function calculateDiscount(price: number, discountPercent: number): number {
    if (discountPercent < 0 || discountPercent > 100) {
        throw new Error("Diskon harus antara 0 dan 100");
    }
    return price - (price * discountPercent / 100);
}

export { formatCurrency, calculateDiscount };
export type { Currency, FormatOptions };


// ===== MENAMBAHKAN TIPE KE LIBRARY LAMA =====
// Jika library tidak punya @types, buat file deklarasi:
// types/my-old-lib.d.ts
declare module "my-old-lib" {
    export function doSomething(input: string): number;
    export function doOther(a: number, b: number): string;
    export const VERSION: string;
}
πŸ’‘ Tips Migrasi Sukses
  • Mulai dengan strict: false, lalu aktifkan satu per satu
  • Gunakan // @ts-ignore untuk sementara melewati error (tapi jangan terlalu banyak!)
  • Install @types/* untuk library yang digunakan
  • Mulai dari file yang paling sering di-edit atau punya bug
  • Gunakan any sementara lalu ganti dengan tipe yang benar
  • Aktifkan allowJs: true agar .js dan .ts bisa berdampingan

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang TypeScript:

Pertanyaan 1: Apa perbedaan utama antara any dan unknown di TypeScript?

a) Tidak ada perbedaan, keduanya sama saja
b) unknown lebih cepat dari any
c) unknown mengharuskan type checking sebelum digunakan, any tidak
d) any hanya untuk number, unknown untuk semua tipe

Pertanyaan 2: Apa kelebihan Interface dibanding Type Alias untuk mendefinisikan object?

a) Interface bisa untuk union type
b) Interface mendukung declaration merging (merge otomatis)
c) Interface lebih cepat saat runtime
d) Interface tidak bisa di-extends

Pertanyaan 3: Apa fungsi dari Generics dalam TypeScript?

a) Membuat kode yang hanya bisa bekerja dengan satu tipe data
b) Menulis kode reusable yang fleksibel tanpa kehilangan type safety
c) Menghapus semua tipe data dari kode
d) Mengkonversi TypeScript ke JavaScript

Pertanyaan 4: Utility type Partial<T> melakukan apa?

a) Menghapus semua property dari tipe T
b) Membuat semua property dari tipe T menjadi wajib (required)
c) Membuat semua property dari tipe T menjadi opsional
d) Membuat semua property dari tipe T menjadi readonly

Pertanyaan 5: Apa yang dilakukan opsi strict: true di tsconfig.json?

a) Mengaktifkan semua pengecekan ketat TypeScript sekaligus
b) Membuat kode berjalan lebih cepat
c) Menghapus komentar dari kode TypeScript
d) Mengubah semua file .js menjadi .ts secara otomatis
πŸ” Zoom
100%
🎨 Tema