Menggunakan TypeScript

TypeScript adalah salah satu cara populer untuk menambahkan definisi type ke dalam basis kode JavaScript. Secara bawaan, TypeScript mendukung JSX dan Anda bisa mendapatkan dukungan penuh React Web dengan menambahkan @types/react dan @types/react-dom ke proyek Anda.

Pemasangan

Semua kerangka kerja React tingkat produksi menawarkan dukungan untuk menggunakan TypeScript. Ikuti panduan khusus kerangka kerja tersebut untuk pemasangan:

Menambahkan TypeScript ke proyek React yang ada

Untuk memasang versi terbaru definisi type React:

Terminal
npm install @types/react @types/react-dom

Opsi compiler berikut perlu disetel dalam tsconfig.json Anda:

  1. dom harus disertakan dalam lib (Catatan: Jika tidak ada opsi lib yang disetel, dom disetel secara bawaan).
  2. jsx harus desetel ke salah satu opsi yang valid. preserve seharusnya cukup untuk sebagian besar aplikasi. Jika Anda menerbitkan pustaka (library), lihat dokumentasi jsx untuk mengetahui opsi yang harus dipilih.

TypeScript dengan Komponen React

Catatan

Setiap file yang berisi JSX harus menggunakan ekstensi file .tsx. Ini adalah ekstensi khusus TypeScript yang memberi tahu TypeScript bahwa file ini berisi JSX.

Menulis TypeScript dengan React sangat mirip dengan menulis JavaScript dengan React. Perbedaan utama saat bekerja dengan komponen adalah Anda dapat menyediakan type untuk props komponen Anda. Type ini dapat digunakan untuk memeriksa kebenaran dan menyediakan dokumentasi sebaris di editor.

Dengan mengambil komponen MyButton dari panduan Mulai Cepat, kita dapat menambahkan type yang mendeskripsikan title untuk tombol:

function MyButton({ title }: { title: string }) {
  return (
    <button>{title}</button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton title="I'm a button" />
    </div>
  );
}

Catatan

Sandbox ini dapat menangani kode TypeScript, tetapi tidak menjalankan pemeriksa type. Ini berarti Anda dapat mengubah sandbox TypeScript untuk belajar, tetapi Anda tidak akan mendapatkan kesalahan (error) atau peringatan (warning) type apa pun. Untuk mendapatkan pemeriksaan type, Anda dapat menggunakan TypeScript Playground atau menggunakan sandbox daring yang berfitur lebih lengkap.

Sintaksis sebaris ini adalah cara paling sederhana untuk menyediakan type bagi suatu komponen, namun setelah Anda mulai memiliki beberapa field untuk mendeskripsikannya, hal ini akan menjadi lebih sulit dibaca. Sebagai gantinya, Anda dapat menggunakan interface atau type untuk mendeskripsikan props sebuah komponen:

interface MyButtonProps {
  /** Teks yang ditampilkan di dalam tombol */
  title: string;
  /** Apakah tombol dapat diklik atau tidak */
  disabled: boolean;
}

function MyButton({ title, disabled }: MyButtonProps) {
  return (
    <button disabled={disabled}>{title}</button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton title="I'm a disabled button" disabled={true}/>
    </div>
  );
}

Type yang mendeskripsikan props komponen Anda dapat sesederhana atau serumit yang Anda perlukan, namun props tersebut harus berupa tipe objek yang dideskripsikan dengan type atau interface. Anda dapat mempelajari tentang cara TypeScript mendeskripsikan objek di Object Types tetapi Anda mungkin juga tertarik menggunakan Union Types untuk mendeskripsikan properti yang dapat berupa salah satu dari beberapa type yang berbeda dan panduan Creating Types from Types untuk kasus penggunaan yang lebih lanjut.

Contoh Hooks

Definisi type dari @types/react menyertakan type untuk Hooks bawaan, sehingga Anda dapat menggunakannya dalam komponen tanpa pengaturan tambahan apa pun. Definisi tersebut dibuat untuk memperhitungkan kode yang Anda tulis dalam komponen, sehingga Anda akan memperoleh type yang disimpulkan sering kali dan idealnya tidak perlu menangani hal-hal sepele dalam menyediakan type sendiri.

Namun, kita dapat melihat beberapa contoh tentang cara menyediakan type untuk Hooks.

useState

Hook useState akan menggunakan kembali nilai yang diberikan sebagai state awal untuk menentukan type dari nilai yang seharusnya. Misalnya:

// Menyimpulkan type sebagai "boolean"
const [enabled, setEnabled] = useState(false);

Ini akan menetapkan type boolean ke enabled, dan setEnabled akan menjadi fungsi yang menerima argumen boolean, atau fungsi yang mengembalikan boolean. Jika Anda ingin secara eksplisit memberikan type untuk state, Anda dapat melakukannya dengan memberikan argumen type ke panggilan useState:

// Menyetel type ke "boolean" secara eksplisit
const [enabled, setEnabled] = useState<boolean>(false);

Hal ini tidak terlalu berguna dalam kasus ini, tetapi kasus umum di mana Anda mungkin ingin memberikan type adalah ketika Anda memiliki union type. Misalnya, status di sini dapat berupa salah satu dari beberapa string yang berbeda:

type Status = "idle" | "loading" | "success" | "error";

const [status, setStatus] = useState<Status>("idle");

Atau, seperti yang direkomendasikan dalam Prinsip-prinsip untuk menata state, Anda dapat mengelompokkan state terkait sebagai objek dan mendeskripsikan berbagai kemungkinan melalui type objek:

type RequestState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success', data: any }
| { status: 'error', error: Error };

const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' });

useReducer

Hook useReducer adalah Hook yang lebih kompleks yang mengambil fungsi reducer dan state awal. Type untuk fungsi reducer disimpulkan dari state awal. Anda dapat secara opsional memberikan argumen type ke panggilan useReducer untuk memberikan type bagi state tersebut, tetapi sering kali lebih baik untuk menetapkan type pada state awal sebagai gantinya:

import {useReducer} from 'react';

interface State {
   count: number 
};

type CounterAction =
  | { type: "reset" }
  | { type: "setCount"; value: State["count"] }

const initialState: State = { count: 0 };

function stateReducer(state: State, action: CounterAction): State {
  switch (action.type) {
    case "reset":
      return initialState;
    case "setCount":
      return { ...state, count: action.value };
    default:
      throw new Error("Unknown action");
  }
}

export default function App() {
  const [state, dispatch] = useReducer(stateReducer, initialState);

  const addFive = () => dispatch({ type: "setCount", value: state.count + 5 });
  const reset = () => dispatch({ type: "reset" });

  return (
    <div>
      <h1>Welcome to my counter</h1>

      <p>Count: {state.count}</p>
      <button onClick={addFive}>Add 5</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

Kita menggunakan TypeScript di beberapa tempat penting:

  • interface State menjelaskan bentuk state dari reducer.
  • type CounterAction menjelaskan berbagai action yang dapat dikirimkan ke reducer.
  • const initialState: State menyediakan type untuk state awal, dan juga type yang digunakan oleh useReducer secara bawaan.
  • stateReducer(state: State, action: CounterAction): State menetapkan type untuk argumen fungsi reducer dan nilai pengembalian.

Alternatif yang lebih eksplisit untuk menetapkan type pada initialState adalah dengan memberikan argumen type ke useReducer:

import { stateReducer, State } from './your-reducer-implementation';

const initialState = { count: 0 };

export default function App() {
const [state, dispatch] = useReducer<State>(stateReducer, initialState);
}

useContext

Hook useContext adalah teknik untuk meneruskan data ke bawah pohon komponen tanpa harus meneruskan props melalui komponen. Teknik ini digunakan dengan membuat komponen penyedia dan sering kali dengan membuat Hook untuk menggunakan nilai dalam komponen anak.

Type dari nilai yang disediakan oleh Context disimpulkan dari nilai yang diteruskan ke panggilan createContext:

import { createContext, useContext, useState } from 'react';

type Theme = "light" | "dark" | "system";
const ThemeContext = createContext<Theme>("system");

const useGetTheme = () => useContext(ThemeContext);

export default function MyApp() {
  const [theme, setTheme] = useState<Theme>('light');

  return (
    <ThemeContext.Provider value={theme}>
      <MyComponent />
    </ThemeContext.Provider>
  )
}

function MyComponent() {
  const theme = useGetTheme();

  return (
    <div>
      <p>Current theme: {theme}</p>
    </div>
  )
}

Teknik ini berfungsi saat Anda memiliki nilai bawaan yang masuk akal - tetapi terkadang ada kasus saat Anda tidak memilikinya, dan dalam kasus tersebut null dapat terasa masuk akal sebagai nilai bawaan. Namun, untuk memungkinkan sistem type memahami kode Anda, Anda perlu secara eksplisit menetapkan ContextShape | null pada createContext.

Hal ini menyebabkan masalah yang mengharuskan Anda menghilangkan | ​​null dalam type untuk consumer Context. Rekomendasi kami adalah agar Hook melakukan pemeriksaan runtime untuk keberadaannya dan memunculkan kesalahan saat tidak ada:

import { createContext, useContext, useState, useMemo } from 'react';

// Ini adalah contoh yang lebih sederhana, namun Anda dapat membayangkan obyek yang lebih kompleks di sini.
type ComplexObject = {
kind: string
};

// Context dibuat dengan `| null` dalam type, untuk merefleksikan nilai bawaan secara akurat.
const Context = createContext<ComplexObject | null>(null);

// `| null` akan dihapus melalui pemeriksaan di dalam Hook.
const useGetComplexObject = () => {
const object = useContext(Context);
if (!object) { throw new Error("useGetComplexObject must be used within a Provider") }
return object;
}

export default function MyApp() {
const object = useMemo(() => ({ kind: "complex" }), []);

return (
<Context.Provider value={object}>
<MyComponent />
</Context.Provider>
)
}

function MyComponent() {
const object = useGetComplexObject();

return (
<div>
<p>Current object: {object.kind}</p>
</div>
)
}

useMemo

Hook useMemo akan membuat/mengakses ulang nilai yang tersimpan dari pemanggilan fungsi, dan menjalankan ulang fungsi hanya saat dependensi yang diteruskan sebagai parameter ke-2 diubah. Hasil pemanggilan Hook disimpulkan dari nilai yang dikembalikan dari fungsi di parameter pertama. Anda dapat lebih eksplisit dengan memberikan argumen type ke Hook.

// Type dari visibleTodos disimpulkan dari nilai kembalian filterTodos
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);

useCallback

useCallback menyediakan referensi stabil ke suatu fungsi selama dependensi yang dimasukkan ke parameter kedua sama. Seperti useMemo, type fungsi disimpulkan dari nilai kembalian fungsi di parameter pertama, dan Anda dapat lebih eksplisit dengan memberikan argumen type ke Hook.

const handleClick = useCallback(() => {
// ...
}, [todos]);

Saat bekerja dalam mode strict TypeScript, useCallback mengharuskan penambahan type untuk parameter dalam callback Anda. Hal ini karena type dari callback disimpulkan dari nilai pengembalian fungsi, dan tanpa parameter, type tersebut tidak dapat dipahami sepenuhnya.

Bergantung pada preferensi gaya kode Anda, Anda dapat menggunakan fungsi *EventHandler dari type React untuk menyediakan type bagi event handler pada saat yang sama saat mendefinisikan callback:

import { useState, useCallback } from 'react';

export default function Form() {
const [value, setValue] = useState("Change me");

const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
setValue(event.currentTarget.value);
}, [setValue])

return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}

Type-type lain yang berguna

Ada sekumpulan type yang cukup luas yang berasal dari package @types/react, ini layak dibaca jika Anda merasa nyaman dengan cara React dan TypeScript berinteraksi. Anda dapat menemukannya di folder React di DefinitelyTyped. Kami akan membahas beberapa type yang lebih umum di sini.

Event DOM

Saat bekerja dengan event DOM di React, type dari event sering kali dapat disimpulkan dari event handler. Namun, saat Anda ingin mengekstrak fungsi untuk diteruskan ke event handler, Anda perlu menetapkan type dari event secara eksplisit.

import { useState } from 'react';

export default function Form() {
  const [value, setValue] = useState("Change me");

  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    setValue(event.currentTarget.value);
  }

  return (
    <>
      <input value={value} onChange={handleChange} />
      <p>Value: {value}</p>
    </>
  );
}

Ada banyak jenis events yang disediakan dalam type React - daftar lengkapnya dapat ditemukan di sini yang didasarkan pada event paling populer dari DOM.

Saat menentukan type yang Anda cari, pertama-tama Anda dapat melihat informasi hover untuk event handler yang Anda gunakan, yang akan menunjukkan tipe event tersebut.

Jika Anda perlu menggunakan event yang tidak termasuk dalam daftar ini, Anda dapat menggunakan type React.SyntheticEvent, yang merupakan type dasar untuk semua event.

Anak (Children)

Ada dua jalur umum untuk mendeskripsikan anak dari suatu komponen. Yang pertama adalah dengan menggunakan type React.ReactNode, yang merupakan gabungan dari semua type yang mungkin yang dapat diteruskan sebagai anak dalam JSX:

interface ModalRendererProps {
title: string;
children: React.ReactNode;
}

Ini adalah definisi anak yang sangat luas. Yang kedua adalah menggunakan type React.ReactElement, yang hanya berupa elemen JSX dan bukan primitif JavaScript seperti string atau angka:

interface ModalRendererProps {
title: string;
children: React.ReactElement;
}

Perlu diingat, bahwa Anda tidak dapat menggunakan TypeScript untuk menjelaskan bahwa anak dari komponen adalah tipe elemen JSX tertentu, jadi Anda tidak dapat menggunakan sistem type untuk menjelaskan komponen yang hanya menerima anak <li>.

Anda dapat melihat contoh React.ReactNode dan React.ReactElement dengan pemeriksa type di playground TypeScript ini.

Props Style

Saat menggunakan inline styles di React, Anda dapat menggunakan React.CSSProperties untuk mendeskripsikan objek yang diteruskan ke props style. Type ini merupakan gabungan dari semua properti CSS yang mungkin, dan merupakan cara yang baik untuk memastikan Anda meneruskan properti CSS yang valid ke properti style, dan untuk mendapatkan pelengkapan otomatis di editor Anda.

interface MyComponentProps {
style: React.CSSProperties;
}

Pembelajaran lebih lanjut

Panduan ini telah membahas dasar-dasar penggunaan TypeScript dengan React, tetapi masih banyak lagi yang dapat dipelajari. Halaman API individual pada dokumentasi mungkin berisi dokumentasi yang lebih mendalam tentang cara menggunakannya dengan TypeScript.

Kami merekomendasikan sumber daya berikut:

  • Buku panduan TypeScript adalah dokumentasi resmi untuk TypeScript, dan mencakup sebagian besar fitur utama dari bahasa pemrograman ini.

  • Catatan rilis TypeScript membahas fitur-fitur baru secara mendalam.

  • React TypeScript Cheatsheet adalah lembar contekan yang dikelola komunitas untuk menggunakan TypeScript dengan React, yang mencakup banyak kasus tepi yang berguna dan menyediakan lebih banyak keluasan daripada dokumen ini.

  • Komunitas Discord TypeScript adalah tempat yang bagus untuk mengajukan pertanyaan dan mendapatkan bantuan terkait masalah TypeScript dan React.