React Native và TypeScript năm 2026: Kiến trúc Type-Safe và Câu hỏi Phỏng vấn

Xây dựng ứng dụng React Native type-safe với TypeScript, Codegen, TurboModules, và Strict TypeScript API. Bao gồm các pattern kiến trúc, typed navigation, yêu cầu toolchain 0.87, và câu hỏi phỏng vấn.

React Native TypeScript Type-Safe Architecture

React Native TypeScript type safety đã trưởng thành đáng kể vào giữa năm 2026, với phiên bản 0.87 biến Strict TypeScript API thành mặc định bắt buộc và hoàn thành việc hiện đại hóa toolchain toàn diện. TypeScript không còn là lớp kết nối tùy chọn giữa JavaScript và native code: nó điều khiển toàn bộ hợp đồng từ component props đến TurboModule interfaces, phát hiện sự không khớp kiểu tại thời điểm build thay vì xuất hiện trong crash logs của môi trường production.

Những thay đổi trong 0.87

React Native 0.87 (tháng 8/2026) biến Strict TypeScript API thành mặc định bắt buộc. Phiên bản này cũng nâng yêu cầu tối thiểu lên Node.js 22.13+, Kotlin 2.0+, và Android Gradle Plugin 9. Tùy chọn opt-out cho legacy deep imports vẫn khả dụng cho đến 0.88.

Tại sao TypeScript Hiện Là Mặc Định Cho Các Dự Án React Native

Mọi lệnh npx react-native init kể từ phiên bản 0.76 đều tạo scaffolding dự án TypeScript. Nhưng sự thay đổi thực sự xảy ra tại ranh giới native. Trước Codegen, các nhà phát triển viết type assertions thủ công khi dữ liệu di chuyển từ JavaScript sang Objective-C hoặc Kotlin, một hợp đồng stringly-typed có thể thất bại âm thầm tại runtime. Codegen đọc các file TypeScript spec và tự động sinh ra các interface C++, Objective-C++, và Java/Kotlin. Nếu TypeScript spec khai báo một method trả về number, native interface được sinh ra sẽ bắt buộc ràng buộc đó tại thời điểm compile.

Tài liệu React Native bao gồm cấu hình cơ bản, nhưng các pattern type-safe quan trọng trong production đi xa hơn: typed navigation stacks, generic API hooks, discriminated unions cho state machines, và TurboModule specs được điều khiển bởi Codegen.

Strict TypeScript API: Từ Opt-In Đến Mặc Định

Strict TypeScript API được giới thiệu trong React Native 0.80 như một tính năng opt-in. Kể từ 0.87, nó trở thành mặc định bắt buộc cho tất cả các dự án mới. Types được sinh trực tiếp từ source code của React Native thay vì được duy trì thủ công, loại bỏ sự lệch pha giữa API được tài liệu hóa và implementation thực tế.

tsconfig.json (cấu hình mặc định 0.87+)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Với cấu hình này, việc import bất cứ thứ gì từ subpath như react-native/Libraries/Text/Text sẽ kích hoạt type error. Tất cả imports phải đến từ root package react-native. Nếu một thư viện hoặc codebase cũ vẫn phụ thuộc vào deep imports, có sẵn tùy chọn opt-out tạm thời cho đến 0.88:

json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "customConditions": ["react-native", "react-native-legacy-deep-imports"]
  }
}

Tùy chọn opt-out này sẽ bị loại bỏ trong 0.89. Việc migration nên được thực hiện trước thời điểm đó.

Yêu Cầu Toolchain Trong 0.87

React Native 0.87 nâng phiên bản tối thiểu trên toàn bộ build chain:

Thành phầnTối thiểu
Node.js22.13.0
Kotlin2.0 (bundled: 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34 (min), 37 (target)

Đối với Android, AGP 9 yêu cầu cấu hình bổ sung:

properties
# android/gradle.properties
android.builtInKotlin=false
android.newDsl=false

Các yêu cầu này áp dụng cho tất cả các dự án nhắm đến 0.87+. Hướng dẫn migration New Architecture bao gồm bối cảnh rộng hơn của những thay đổi này.

Type-Safe Navigation Với React Navigation 7

React Navigation 7.x cung cấp hỗ trợ TypeScript first-class. Pattern chính: định nghĩa kiểu RootStackParamList ánh xạ mọi tên screen đến params mong đợi, sau đó truyền kiểu đó qua navigators và screen components.

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;
};

Screen components sau đó nhận typed props mà không cần casting thủ công:

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' }) is type-checked
  // navigation.navigate('Profile', {}) causes a compile error: missing userId
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

Cách tiếp cận này loại bỏ toàn bộ một class các runtime errors: điều hướng đến screen với params sai hoặc thiếu sẽ thất bại tại thời điểm build.

Định kiểu hook useNavigation

Đối với các components không phải là screen children trực tiếp, sử dụng useNavigation<NativeStackNavigationProp<RootStackParamList>>() để có được type safety tương tự mà không cần prop drilling.

Xây Dựng TurboModule Type-Safe Với Codegen

TurboModules thay thế hệ thống Native Modules cũ. File TypeScript spec đóng vai trò là nguồn sự thật duy nhất, và Codegen sinh ra các native interfaces từ nó. Nếu spec và native implementation khác nhau, build sẽ thất bại.

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');

Chạy npx react-native codegen sinh ra các interface C++, Objective-C++, và Java tương ứng. Native implementation phải khớp chính xác mọi method signature. Ví dụ, getStorageInfo phải trả về object với ba trường số; trả về shape khác sẽ gây compile-time error ở phía native.

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)
    }
}

Cách tiếp cận này loại bỏ việc parsing ReadableMap và NSDictionary từng gây ra các bug silent type coercion trong kiến trúc cũ. Để tìm hiểu sâu hơn về hệ thống native module, xem câu hỏi phỏng vấn native modules.

Sẵn sàng chinh phục phỏng vấn React Native?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Generic Data Fetching Với Typed API Hooks

Một pattern typed hook có thể tái sử dụng tránh trùng lặp logic fetch trên các screens trong khi giữ nguyên type inference đầy đủ:

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

Generic parameter T chảy qua toàn bộ chuỗi: từ điểm gọi hook, qua query function, đến component tiêu thụ kết quả. Không có as casts, không có any types.

Discriminated Unions Cho State Machines

Các trạng thái screen phức tạp (loading, error, empty, loaded) nên được mô hình hóa dưới dạng discriminated unions thay vì một tập hợp các optional fields:

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);
  }
}

Pattern này làm cho các impossible states không thể biểu diễn được. Trạng thái loading không thể vô tình mang theo data cũ, và trạng thái error luôn bao gồm ngữ cảnh để debug.

Tránh partial state objects

Một anti-pattern phổ biến: { isLoading: boolean; error?: string; data?: T }. Pattern này cho phép các trạng thái như { isLoading: true, error: 'fail', data: [...] }, ba tín hiệu mâu thuẫn cùng một lúc. Discriminated unions ngăn chặn điều này ở cấp độ type.

Câu Hỏi Phỏng Vấn React Native TypeScript

Những câu hỏi này phản ánh những gì các đội mobile engineering cấp cao hỏi trong các cuộc phỏng vấn năm 2026, khi New Architecture và TypeScript đã trở thành tiêu chuẩn.

Codegen thực thi type safety qua ranh giới JavaScript-native như thế nào?

Codegen đọc các file TypeScript (hoặc Flow) spec và sinh ra mã interface C++, Objective-C++, và Java/Kotlin. Các native interface được sinh ra thực thi chính xác các method signatures, parameter types, và return types được định nghĩa trong spec. Nếu native implementation lệch (trả về Int khi spec khai báo Double, hoặc bỏ qua một field từ struct), native compiler sẽ từ chối build. Điều này chuyển các lỗi type từ runtime crashes sang build-time failures.

Strict TypeScript API là gì và tại sao nó quan trọng?

Strict TypeScript API sinh types trực tiếp từ source code của React Native thay vì duy trì các file .d.ts viết tay. Trong 0.87, nó trở thành mặc định bắt buộc. Nó giới hạn imports đến root package react-native, deprecate deep imports. Điều này định nghĩa một API surface công khai ổn định: các refactoring nội bộ không thể phá vỡ mã consumer nếu consumers chỉ sử dụng strict types. Tùy chọn opt-out tạm thời qua customConditions vẫn khả dụng cho đến 0.88.

Cách định kiểu React Navigation params qua nested navigators như thế nào?

Định nghĩa kiểu ParamList cho mỗi navigator và kết hợp chúng bằng NavigatorScreenParams. Đối với tab navigator lồng bên trong stack, param list của stack tham chiếu đến tab's: type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }. Mọi lệnh gọi navigate() sau đó được kiểm tra type qua toàn bộ hệ thống phân cấp lồng nhau, bắt tên screen sai hoặc params thiếu tại compile time.

Discriminated unions giải quyết vấn đề gì trong React Native state management?

Discriminated unions mô hình hóa các trạng thái loại trừ lẫn nhau (loading, error, loaded) như các nhánh riêng biệt của union type, được khóa bởi trường status. TypeScript thu hẹp kiểu trong mỗi nhánh của switch statement, vì vậy việc truy cập state.data chỉ có thể khi state.status === 'loaded'. Điều này ngăn chặn các impossible states như loading indicator hiển thị cùng với error data, một class bug mà optional fields và boolean flags không thể ngăn chặn.

Giải thích sự khác biệt giữa TurboModules và hệ thống Native Modules cũ.

Native Modules giao tiếp qua async bridge, serialize tất cả dữ liệu thành JSON. TurboModules sử dụng JSI (JavaScript Interface) cho các lệnh gọi C++ đồng bộ, trực tiếp mà không có serialization overhead. Chúng cũng tải lazy (khi sử dụng lần đầu thay vì khi khởi động app, giảm cold start time) và sử dụng Codegen để sinh các interface type-safe từ TypeScript specs. Hệ thống cũ dựa vào parsing ReadableMap / NSDictionary với runtime type coercion; TurboModules thực thi types tại compile time.

Những API nào đã bị loại bỏ trong React Native 0.87?

0.87 loại bỏ InteractionManager (sử dụng requestIdleCallback), các kiểu NativeMethods / NativeMethodsMixin (sử dụng HostInstance), và một số props StatusBar. Deep imports đến các đường dẫn src/private/ giờ là type errors. Cờ useTurboModules cũng đã bị loại bỏ vì TurboModules giờ luôn được bật.

Để xem thêm câu hỏi phỏng vấn React Native, hướng dẫn đầy đủ bao gồm các chủ đề về kiến trúc, hiệu suất, và debugging.

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Sources

Điều Cần Nhớ Về React Native TypeScript Năm 2026

  • Strict TypeScript API (bắt buộc trong 0.87) giới hạn imports đến API surface công khai ổn định, ngăn chặn sự phá vỡ từ các thay đổi nội bộ. Tùy chọn opt-out cho legacy deep imports chỉ khả dụng cho đến 0.88.
  • Codegen sinh native interfaces từ các file TypeScript spec, chuyển các lỗi type từ runtime crashes sang build-time failures qua ranh giới JS-native
  • Yêu cầu toolchain tối thiểu trong 0.87: Node.js 22.13+, Kotlin 2.0+, AGP 9, compileSdk 34+
  • Typed navigation params qua RootStackParamList và NativeStackScreenProps bắt tên screen sai và params thiếu trước khi app chạy
  • Discriminated unions mô hình hóa các trạng thái screen như các nhánh loại trừ lẫn nhau, làm cho impossible states không thể biểu diễn ở cấp độ type
  • TurboModules với typed specs thay thế việc parsing ReadableMap / NSDictionary cũ, thực thi type safety đầy đủ từ JavaScript qua C++ đến mã platform-native
  • Generic API hooks với TanStack Query giữ nguyên type inference từ endpoint đến component mà không cần casts thủ công

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Thử thách hôm nay

Bạn có tìm ra lỗi trong React Native không?

Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Anthony Fillion-Maillet

Viết bởi

Anthony Fillion-Maillet

Người sáng lập SharpSkill

Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.

Cập nhật ngày 25 tháng 8, 2026

Thẻ

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

Chia sẻ

Bài viết liên quan