React Native dan TypeScript 2026: Arsitektur Type-Safe, Codegen, serta Pertanyaan Wawancara Teknis

Pelajari arsitektur type-safe React Native dengan TypeScript di 2026: konfigurasi Strict API, typed navigation, TurboModules via Codegen, discriminated unions, dan pertanyaan wawancara mobile developer.

React Native TypeScript Type-Safe Architecture

Ekosistem React Native mengalami transformasi fundamental sepanjang paruh pertama 2026. Versi 0.86 yang dirilis Juni 2026 membawa React 19.1, arsitektur bridgeless secara default, dan Strict TypeScript API yang menjadikan TypeScript bukan sekadar pelengkap melainkan fondasi kontrak antara lapisan JavaScript dan native. Codegen kini membaca spesifikasi TypeScript untuk menghasilkan interface C++, Objective-C++, dan Kotlin secara otomatis, sehingga ketidaksesuaian tipe terdeteksi saat kompilasi, bukan saat aplikasi sudah berjalan di perangkat pengguna. Artikel ini mengupas pola arsitektur type-safe yang relevan untuk produksi, dilengkapi contoh kode dan pertanyaan wawancara teknis yang kerap diajukan pada tahun 2026.

Perubahan penting di 2026

React Native 0.80 memperkenalkan Strict TypeScript API secara opt-in, versi 0.82 menghapus legacy bridge secara permanen, dan versi 0.85 menghilangkan interop layer terakhir. Per versi 0.86 (Juni 2026), setiap proyek baru dimulai dengan arsitektur bridgeless penuh dan TypeScript types yang di-generate langsung dari source code.

Mengapa TypeScript Menjadi Pilihan Default untuk React Native

Sejak React Native 0.76, perintah npx react-native init secara otomatis menghasilkan scaffolding proyek TypeScript. Perubahan tersebut bukan semata soal preferensi developer, melainkan kebutuhan arsitektural. Pada era legacy bridge, komunikasi antara JavaScript dan kode native bergantung pada serialisasi JSON dan parsing manual melalui ReadableMap di Android serta NSDictionary di iOS. Kontrak berbasis string semacam ini rentan gagal secara diam-diam saat runtime.

Hadirnya Codegen mengubah paradigma tersebut secara mendasar. Codegen membaca file spesifikasi TypeScript dan menghasilkan interface native yang mengikat secara ketat. Apabila sebuah method dideklarasikan mengembalikan number dalam spec TypeScript, interface native yang dihasilkan memaksa implementasi platform mengembalikan tipe numerik yang sesuai. Ketidakcocokan tipe langsung menyebabkan kegagalan kompilasi.

Dokumentasi resmi React Native mencakup konfigurasi dasar TypeScript. Namun pola-pola yang dibutuhkan dalam aplikasi produksi melangkah lebih jauh: typed navigation stacks, generic API hooks dengan TanStack Query, discriminated unions untuk manajemen state, serta TurboModule specs yang digerakkan Codegen.

Mengonfigurasi Strict TypeScript API

Strict TypeScript API yang diperkenalkan pada React Native 0.80 membatasi surface area API publik hanya pada types yang dihasilkan langsung dari source code framework. Fitur ini mencegah developer secara tidak sengaja bergantung pada modul internal yang dapat berubah sewaktu-waktu antar rilis minor.

tsconfig.jsonjson
{
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true,
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "types": ["react-native/types/strict"]
  },
  "extends": "@react-native/typescript-config/tsconfig.json"
}

Setelah konfigurasi di atas diterapkan, setiap upaya mengimpor dari subpath internal seperti react-native/Libraries/Text/Text akan menghasilkan type error. Seluruh import wajib berasal dari root package react-native, selaras dengan kebijakan deep-import deprecation yang berlaku sejak versi 0.80.

Beberapa opsi compiler yang perlu diperhatikan secara khusus: exactOptionalPropertyTypes melarang assignment undefined secara eksplisit pada properti opsional kecuali tipe tersebut memang menyertakan undefined dalam union-nya. Sementara itu, noUncheckedIndexedAccess secara otomatis menambahkan undefined pada setiap operasi akses melalui indeks, sehingga developer dipaksa melakukan null check sebelum menggunakan nilai yang diakses via bracket notation.

Type-Safe Navigation dengan React Navigation 7

React Navigation 7.x menawarkan dukungan TypeScript yang matang. Inti dari pendekatan ini adalah mendefinisikan tipe ParamList yang memetakan setiap nama screen ke parameter yang diekspektasi, kemudian meneruskan tipe tersebut ke seluruh navigator dan screen component.

navigation/types.tstypescript
export type RootStackParamList = {
  Home: undefined;
  Profile: { userId: string };
  Settings: undefined;
  ArticleDetail: { articleId: string; source: 'feed' | 'search' };
};

export type AppTabParamList = {
  Dashboard: undefined;
  Explore: { category?: string };
  Notifications: undefined;
};

Pada definisi di atas, setiap key mewakili nama screen dan value-nya mewakili parameter yang wajib disertakan saat navigasi. Screen Home serta Settings tidak memerlukan parameter (undefined). Screen Profile mewajibkan userId bertipe string, sedangkan ArticleDetail memerlukan articleId beserta source yang dibatasi pada literal union 'feed' | 'search'.

Komponen screen kemudian menerima props yang sudah ter-type secara otomatis tanpa perlu melakukan casting manual:

screens/ArticleDetailScreen.tsxtypescript
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/types';

type Props = NativeStackScreenProps<RootStackParamList, 'ArticleDetail'>;

export function ArticleDetailScreen({ route, navigation }: Props) {
  // route.params.articleId is string — guaranteed by the type
  // route.params.source is 'feed' | 'search' — no runtime check needed
  const { articleId, source } = route.params;

  // navigation.navigate('Profile', { userId: '123' }) — type-checked
  // navigation.navigate('Profile', {}) — compile error: missing userId
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Manfaat utama dari pendekatan ini adalah menghilangkan satu kategori besar error runtime. Kesalahan navigasi ke screen dengan parameter yang tidak sesuai atau tidak lengkap akan terdeteksi pada saat build, jauh sebelum kode tersebut dijalankan di perangkat pengguna akhir.

Mengetik useNavigation hook

Untuk komponen yang bukan children langsung dari screen, gunakan useNavigation<NativeStackNavigationProp<RootStackParamList>>() untuk mendapatkan type safety yang sama tanpa perlu melakukan prop drilling.

Membangun TurboModule Type-Safe dengan Codegen

TurboModules hadir sebagai pengganti sistem Native Modules lama yang berkomunikasi melalui async bridge. Dalam arsitektur baru, file spesifikasi TypeScript berperan sebagai single source of truth. Codegen membaca file tersebut dan menghasilkan abstract class native yang wajib diimplementasikan oleh kode platform. Apabila spesifikasi dan implementasi tidak sinkron, proses build akan gagal.

specs/NativeDeviceInfo.tstypescript
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  getDeviceModel(): string;
  getBatteryLevel(): Promise<number>;
  getStorageInfo(): Promise<{
    totalBytes: number;
    freeBytes: number;
    usedPercentage: number;
  }>;
  onBatteryChange(callback: (level: number) => void): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');

Spesifikasi di atas mendeskripsikan kontrak yang eksplisit: getDeviceModel() mengembalikan string secara sinkron, getBatteryLevel() mengembalikan Promise<number> secara asinkron, dan getStorageInfo() mengembalikan Promise berisi objek dengan tiga field numerik. Menjalankan npx react-native codegen akan menghasilkan interface C++, Objective-C++, dan Java yang harus dipatuhi oleh implementasi native di setiap platform.

Berikut contoh implementasi di sisi Android menggunakan Kotlin:

android/app/src/main/java/com/app/DeviceInfoModule.ktkotlin
class DeviceInfoModule(reactContext: ReactApplicationContext) :
    NativeDeviceInfoSpec(reactContext) {

    // Return type enforced by generated NativeDeviceInfoSpec
    override fun getDeviceModel(): String {
        return Build.MODEL
    }

    override fun getBatteryLevel(): Promise<Double> {
        val bm = reactContext.getSystemService(Context.BATTERY_SERVICE)
            as BatteryManager
        val level = bm.getIntProperty(
            BatteryManager.BATTERY_PROPERTY_CAPACITY
        ).toDouble()
        return Promise.resolve(level)
    }
}

Class NativeDeviceInfoSpec dihasilkan oleh Codegen berdasarkan spesifikasi TypeScript. Compiler Kotlin akan menolak kode yang tidak memenuhi kontrak tersebut. Sebagai contoh, mengembalikan Int alih-alih Double untuk getBatteryLevel() akan langsung menghasilkan compile error. Pendekatan ini secara efektif menghilangkan bug silent type coercion yang umum terjadi pada era parsing ReadableMap dan NSDictionary.

Siap menguasai wawancara React Native Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Generic Data Fetching dengan Typed API Hooks

Aplikasi React Native pada umumnya memiliki banyak screen yang melakukan fetch data dari REST API. Tanpa abstraksi yang tepat, logika fetch akan terduplikasi di setiap screen. Pola typed hook generik berikut mengatasi permasalahan tersebut sambil mempertahankan type inference secara penuh:

hooks/useApiQuery.tstypescript
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

interface ApiResponse<T> {
  data: T;
  meta: { page: number; totalPages: number };
}

export function useApiQuery<T>(
  key: readonly string[],
  endpoint: string,
  options?: Omit<UseQueryOptions<ApiResponse<T>>, 'queryKey' | 'queryFn'>
) {
  return useQuery<ApiResponse<T>>({
    queryKey: key,
    queryFn: async () => {
      const response = await fetch(`${API_BASE}${endpoint}`);
      if (!response.ok) throw new ApiError(response.status);
      return response.json() as Promise<ApiResponse<T>>;
    },
    ...options,
  });
}

// Usage — T is inferred as Article[]
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

const { data, isLoading } = useApiQuery<Article[]>(
  ['articles', 'latest'],
  '/articles?sort=latest'
);
// data.data is Article[] — fully typed
// data.meta.totalPages is number

Parameter generik T mengalir dari titik pemanggilan hook hingga ke komponen yang mengonsumsi hasilnya. Ketika developer memanggil useApiQuery<Article[]>(...), TypeScript secara otomatis menyimpulkan bahwa data.data bertipe Article[] dan data.meta.totalPages bertipe number. Tidak diperlukan as cast maupun any type di mana pun dalam chain tersebut.

Penggunaan Omit untuk mengecualikan queryKey dan queryFn dari parameter opsi mencegah consumer hook menimpa logika inti secara tidak disengaja. Di sisi lain, konfigurasi seperti staleTime, enabled, dan refetchInterval tetap dapat dikustomisasi sesuai kebutuhan masing-masing screen.

Discriminated Unions untuk State Machines

Mengelola state layar yang kompleks dengan boolean flags seperti isLoading, isError, dan hasData merupakan pendekatan yang rawan masalah. Kombinasi flags tersebut dapat menghasilkan state yang secara logika mustahil. Discriminated unions menawarkan solusi yang lebih aman di level type system:

types/screen-state.tstypescript
type ScreenState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'error'; error: string; retryCount: number }
  | { status: 'empty'; message: string }
  | { status: 'loaded'; data: T; refreshedAt: Date };

// components/DataScreen.tsx
function renderContent<T>(state: ScreenState<T>, renderItem: (data: T) => ReactNode) {
  switch (state.status) {
    case 'idle':
      return null;
    case 'loading':
      return <LoadingSpinner />;
    case 'error':
      // state.error is string here — TypeScript narrows automatically
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data is T — fully typed
      return renderItem(state.data);
  }
}

Setiap branch dalam union merepresentasikan satu state yang eksklusif. State loading tidak mungkin membawa properti data, dan state error selalu memiliki informasi retryCount untuk keperluan debugging. TypeScript melakukan type narrowing secara otomatis di dalam setiap case, sehingga akses terhadap properti yang tidak relevan dengan state tersebut akan menghasilkan compile error.

Hindari pola partial state objects

Anti-pattern yang umum dijumpai: { isLoading: boolean; error?: string; data?: T }. Pola ini memungkinkan kombinasi state seperti { isLoading: true, error: 'fail', data: [...] } yang mengandung tiga sinyal kontradiktif secara bersamaan. Discriminated unions mencegah kemungkinan tersebut di level type system.

Pertanyaan Wawancara React Native TypeScript

Pertanyaan-pertanyaan berikut merepresentasikan topik yang kerap diuji oleh tim engineering senior pada wawancara mobile developer di tahun 2026, seiring New Architecture dan TypeScript telah menjadi standar industri.

Bagaimana mekanisme Codegen dalam menegakkan type safety di batas JavaScript-native?

Codegen membaca file spesifikasi TypeScript (atau Flow) lalu menghasilkan kode interface untuk C++, Objective-C++, dan Java/Kotlin. Interface native yang dihasilkan memaksakan kecocokan persis pada method signatures, parameter types, dan return types sebagaimana didefinisikan dalam spec. Apabila implementasi native menyimpang dari kontrak tersebut, misalnya mengembalikan Int padahal spec mendeklarasikan Double atau menghilangkan field dari sebuah struct, compiler native akan menolak build. Mekanisme ini menggeser deteksi error tipe dari runtime crashes menjadi build-time failures.

Apa yang dimaksud dengan Strict TypeScript API dan apa signifikansinya?

Diperkenalkan pada React Native 0.80, Strict TypeScript API menghasilkan type definitions langsung dari source code React Native, menggantikan pendekatan lama yang mengandalkan file .d.ts buatan tangan. API ini membatasi seluruh import ke root package react-native dan mendepresiasi deep imports. Implikasi utamanya: refactoring internal framework tidak akan merusak kode consumer selama consumer hanya menggunakan strict types. Pengaktifannya dilakukan melalui opsi "types": ["react-native/types/strict"] di dalam tsconfig.json.

Bagaimana cara menerapkan typing pada React Navigation params di nested navigators?

Setiap navigator memerlukan tipe ParamList tersendiri, kemudian digabungkan menggunakan NavigatorScreenParams. Untuk skenario tab navigator yang bersarang di dalam stack navigator, param list stack harus mereferensikan tipe tab: type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Setiap pemanggilan navigate() akan diperiksa tipe-nya menelusuri seluruh hierarki nesting, sehingga nama screen yang keliru atau parameter yang kurang langsung terdeteksi pada compile time.

Permasalahan apa yang diselesaikan oleh discriminated unions dalam state management React Native?

Discriminated unions memodelkan state yang bersifat mutually exclusive (loading, error, loaded) sebagai branch-branch terpisah dari sebuah union type, dengan field status sebagai diskriminator. TypeScript melakukan narrowing tipe di setiap branch switch statement, sehingga akses terhadap state.data hanya dimungkinkan ketika state.status === 'loaded'. Pendekatan ini mencegah state yang mustahil, seperti loading indicator yang tampil bersamaan dengan data error, yang merupakan kategori bug yang tidak dapat dicegah oleh kombinasi optional fields dan boolean flags.

Jelaskan perbedaan mendasar antara TurboModules dan sistem Native Modules lama.

Native Modules lama berkomunikasi melalui async bridge dengan men-serialize seluruh data ke format JSON. TurboModules menggunakan JSI (JavaScript Interface) untuk melakukan pemanggilan C++ secara langsung dan sinkron tanpa overhead serialisasi. TurboModules juga memuat secara lazy, yaitu hanya ketika pertama kali digunakan, bukan saat startup aplikasi, sehingga mengurangi cold start time secara signifikan. Dari perspektif type safety, TurboModules menggunakan Codegen untuk menghasilkan type-safe interfaces dari TypeScript specs, sedangkan sistem lama mengandalkan parsing ReadableMap / NSDictionary yang rentan terhadap runtime type coercion.

Untuk pertanyaan wawancara React Native lainnya, tersedia pembahasan mendalam mengenai JSI, Fabric, dan arsitektur bridgeless.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Kesimpulan

Berikut rangkuman pola arsitektur type-safe React Native TypeScript yang perlu dikuasai di tahun 2026:

  • Strict TypeScript API (0.80+) membatasi import ke surface area API publik yang stabil, sehingga perubahan internal framework tidak berdampak pada kode consumer. Fitur ini sebaiknya diaktifkan di setiap proyek baru
  • Codegen menghasilkan native interfaces dari file spesifikasi TypeScript, menggeser deteksi error tipe dari runtime crashes ke build-time failures di batas JavaScript-native
  • Typed navigation params melalui RootStackParamList dan NativeStackScreenProps menangkap kesalahan nama screen serta parameter yang tidak lengkap sebelum aplikasi dijalankan
  • Discriminated unions memodelkan state screen sebagai branch yang mutually exclusive, membuat kombinasi state yang mustahil tidak dapat direpresentasikan di level type system
  • TurboModules dengan typed specs menggantikan parsing ReadableMap dan NSDictionary, menegakkan type safety penuh dari JavaScript melalui C++ hingga kode platform native
  • Generic API hooks dengan TanStack Query mempertahankan type inference dari endpoint hingga komponen UI tanpa memerlukan cast manual

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tag

#react-native
#typescript
#mobile-development
#turbomodules
#interview

Bagikan

Artikel terkait