JavaScript dynamic typing kasih kebebasan, tapi juga sumber bug runtime. Fungsi terima string, dipanggil dengan number — gak ada yang protes sampai user klik dan crash. Pas project besar (10+ developer, 100+ component, ratusan endpoint API), bug type setting ngebakar productivitas. TypeScript = JavaScript + static type checking. Tulis kode dengan type annotations, IDE catch error sebelum kamu push commit, refactor jadi safer (rename variable: tools tahu di mana saja referensi-nya). React + TypeScript = 2026 default modern di kebanyakan project production.
💡 Investasi yang berbayar. Belajar TypeScript butuh 2-3 bulan ekstra effort. Tapi setelah produktif, kamu gak akan mau balik ke vanilla JavaScript untuk project serius. Auto-complete props lebih cepat, refactor PD, runtime error berkurang drastis. Skill TS = lowongan kerja Indonesia naik premium 30-40%.
Props dengan Interface/Type
interface UserCardProps {
nama: string
umur: number
isAdmin?: boolean // opsional
onClick?: () => void
}
function UserCard({ nama, umur, isAdmin = false, onClick }: UserCardProps) {
return <div onClick={onClick}>{nama} ({umur})</div>
}
type vs interface — di kebanyakan kasus interchangeable. Convention modern: type untuk props (lebih flexible), interface untuk public API (bisa di-extend).
State dengan Generics
const [count, setCount] = useState<number>(0) // explicit
const [count, setCount] = useState(0) // inferred → number
const [user, setUser] = useState<User | null>(null) // union
const [items, setItems] = useState<string[]>([]) // array
const [data, setData] = useState<{ id: number, name: string }[]>([])
TypeScript infer dari initial value — gak perlu explicit kalau initial type clear.
Event Types — Tau Mana Yang Mana
// Mouse events
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget.disabled) // type-safe
}
// Change events
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value)
}
// Form submit
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
// Keyboard
const handleKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") submitForm()
}
Inline arrow di JSX biasanya inferred:
<button onClick={(e) => /* e is React.MouseEvent<HTMLButtonElement> */} />
Children Type
interface LayoutProps {
children: React.ReactNode // ← pakai ini, paling permissive (string, JSX, array, null)
title: string
}
// Alternatif:
// React.ReactElement — JSX only
// React.ReactChild — string atau JSX
// JSX.Element — single JSX (bukan fragment)
Custom Hook Type
function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial)
const toggle = () => setValue(v => !v)
return [value, toggle]
}
// Atau pakai object return (preferred modern)
function useToggle(initial = false) {
const [value, setValue] = useState(initial)
const toggle = () => setValue(v => !v)
return { value, toggle }
}
Modern style: object return, type inferred otomatis, gak perlu manual annotation.
Hindari React.FC<Props>
// ❌ Pattern lama, gak direkomendasi lagi
const Button: React.FC<Props> = ({ label }) => <button>{label}</button>
// ✓ Modern style
function Button({ label }: Props) {
return <button>{label}</button>
}
Tim React resmi gak rekomendasi React.FC lagi — gak kasih type safety extra, gak intuitif untuk junior, return type bisa di-infer dari function biasa.
Generic Components
interface ListProps<T> {
items: T[]
renderItem: (item: T) => React.ReactNode
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map((item, i) => <li key={i}>{renderItem(item)}</li>)}</ul>
}
// Pemakaian — type T di-infer dari items
<List items={users} renderItem={(user) => user.name} />
// ^User[] ^user: User
Discriminated Union — Pattern Powerful
type FetchState<T> =
| { status: "loading" }
| { status: "error"; error: string }
| { status: "success"; data: T }
function UserList({ state }: { state: FetchState<User[]> }) {
if (state.status === "loading") return <Spinner />
if (state.status === "error") return <Error message={state.error} />
// di sini TypeScript TAU state.data ada (narrowing)
return <ul>{state.data.map(...)}</ul>
}
🎭 Analogi sehari-hari
TypeScript itu kayak kontrak kerja yang jelas. JavaScript = kerja informal — "udah deh, garap dulu", deadline molor, output beda dari ekspektasi, harus ulang dari awal. TypeScript = kontrak hitam-putih — spec dijelasin di awal (interface props), output harus match (return type), kalau ada perubahan harus diskusi di awal (refactor). Setup-nya lebih lama (belajar syntax, define types), tapi pas project udah jalan, sebagian besar miscommunication langsung kelihatan di IDE merah — kamu fix sebelum jadi bug production. Tim besar (10+ orang) di Tokopedia/Gojek tanpa TypeScript = chaos. Project pribadi quick prototype OK pake JS.
⚠️ Jebakan yang sering ditemui
anyeverywhere — defeats the purpose. TypeScript jadi mahal komentar tanpa benefit. Pakaiunknownkalau benar-benar gak tau, dan narrow nanti.ascasting tanpa cek —data as Userbypass type check. Bisa runtime crash. Pakai type guard atau Zod parsing.- Type props yang complex pakai object literal inline —
function X({ a }: { a: number, b: string })susah refactor. Extract ketype Props = {...}. - Lupa
null/undefineddi union —useState<User>(null)error. PakaiuseState<User | null>(null). - Event type wrong —
onChange={(e: MouseEvent) => ...}di input = wrong. PakaiReact.ChangeEvent<HTMLInputElement>. React.FCmasih dipake — pattern lama, hindari di kode baru.- Skip type imports —
import { User } from ...vsimport type { User } from .... Pakaitypekeyword untuk type-only imports (faster build).
🎯 Type props — pilih
typeatauinterface?
- Component props →
type(lebih flexible, support union, intersection, conditional)- Public API library →
interface(bisa di-extend di consumer side)- Function signature, callback type →
type- Object shape sederhana → keduanya OK, pilih konsisten dalam project
Aturan project hypersheets-frontend: pakai
typeuntuk props default,interfacesaat butuh declaration merging.
TL;DR: TypeScript = JavaScript + static type check, catch bug sebelum runtime. Stack modern React 2026: TS default. Props pakai type/interface, state useState<T>(...), event React.ChangeEvent<HTMLInputElement>, children React.ReactNode. Hindari React.FC (pattern lama), any (defeats purpose), as casting tanpa cek. Generic components untuk reusable like <List<T>>. Discriminated union powerful untuk state machine pattern.