React Native và TypeScript năm 2026: Kiến trúc Type-Safe và Câu hỏi Phỏng vấn
Hướng dẫn chuyên sâu về kiến trúc type-safe trong React Native với TypeScript năm 2026: Strict API, Codegen, TurboModules, typed navigation, generic hooks và câu hỏi phỏng vấn kỹ thuật.

Tính đến giữa năm 2026, React Native phiên bản 0.86 đã tích hợp React 19.1, Strict TypeScript API và kiến trúc bridgeless hoàn chỉnh được điều khiển bởi Codegen. TypeScript không còn đơn thuần là lớp kết nối giữa JavaScript và mã native mà đã trở thành xương sống của toàn bộ hợp đồng dữ liệu, từ props của component cho đến interface của TurboModule. Mọi sự sai lệch về kiểu dữ liệu đều được phát hiện tại thời điểm biên dịch thay vì xuất hiện trong nhật ký crash của môi trường sản xuất. Bài viết này phân tích toàn diện các mẫu thiết kế type-safe đang được áp dụng rộng rãi trong các dự án React Native chuyên nghiệp, đồng thời cung cấp câu hỏi phỏng vấn giúp lập trình viên chuẩn bị cho các vị trí phát triển mobile trong năm nay.
React Native 0.80 giới thiệu Strict TypeScript API dạng opt-in. Phiên bản 0.82 loại bỏ vĩnh viễn legacy bridge. Phiên bản 0.85 xóa bỏ lớp interop cuối cùng. Tính đến phiên bản 0.86 (tháng 6/2026), mọi dự án mới đều bắt đầu với kiến trúc bridgeless đầy đủ và TypeScript types được sinh trực tiếp từ mã nguồn.
Tại sao TypeScript trở thành chuẩn mặc định cho React Native
Từ phiên bản 0.76, mọi lệnh npx react-native init đều tạo ra dự án TypeScript mặc định. Tuy nhiên, bước ngoặt thực sự xảy ra tại ranh giới giữa JavaScript và mã native. Trước khi có Codegen, các nhà phát triển phải viết type assertion thủ công khi dữ liệu đi qua lại giữa JavaScript và Objective-C hoặc Kotlin. Đây là một hợp đồng stringly-typed có thể thất bại âm thầm tại runtime mà không có bất kỳ cảnh báo nào.
Codegen giải quyết triệt để vấn đề này bằng cách đọc các file TypeScript spec và tự động sinh ra interface cho C++, Objective-C++ và Java/Kotlin. Nếu TypeScript spec khai báo một phương thức trả về kiểu number, thì native interface được sinh ra sẽ bắt buộc ràng buộc này tại thời điểm biên dịch.
Tài liệu React Native cung cấp hướng dẫn cấu hình cơ bản, nhưng các mẫu thiết kế type-safe thực sự quan trọng trong môi trường production đòi hỏi nhiều hơn thế: typed navigation stacks, generic API hooks, discriminated unions cho state machines và TurboModule specs được dẫn dắt bởi Codegen.
Từ góc độ kỹ thuật phần mềm, strict TypeScript giúp phát hiện hàng loạt lỗi ngay tại thời điểm biên dịch mà trước đây chỉ bộc lộ khi chạy ứng dụng trên thiết bị thực. Trong lĩnh vực mobile, nơi mỗi bản cập nhật phải trải qua quy trình review của App Store và Google Play, việc bắt lỗi sớm mang giá trị đặc biệt lớn.
Thiết lập cấu hình Strict TypeScript API
Strict TypeScript API được giới thiệu từ React Native 0.80, giới hạn phạm vi API công khai chỉ bao gồm các types được sinh trực tiếp từ mã nguồn. Điều này ngăn chặn việc vô tình phụ thuộc vào các module nội bộ có thể thay đổi giữa các phiên bản minor.
{
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["react-native/types/strict"]
},
"extends": "@react-native/typescript-config/tsconfig.json"
}Với cấu hình trên, bất kỳ lệnh import nào từ subpath như react-native/Libraries/Text/Text sẽ gây ra type error. Toàn bộ imports phải xuất phát từ root package react-native, phù hợp với chính sách deep-import deprecation được áp dụng từ phiên bản 0.80.
Cờ strict: true kích hoạt đồng thời strictNullChecks, strictFunctionTypes, strictBindCallApply và nhiều kiểm tra khác. Cờ exactOptionalPropertyTypes ngăn việc gán giá trị undefined một cách tường minh cho optional property, buộc lập trình viên phân biệt rõ ràng giữa "thuộc tính không tồn tại" và "thuộc tính có giá trị undefined". Cờ noUncheckedIndexedAccess đảm bảo mọi truy cập mảng hoặc object bằng index đều trả về T | undefined, tránh lỗi runtime khi truy cập phần tử không tồn tại.
Type-Safe Navigation với React Navigation 7
Hệ thống điều hướng (navigation) là một trong những thành phần phức tạp nhất trong ứng dụng React Native, đồng thời cũng là nơi lỗi runtime thường xuất hiện nhiều nhất. Một tham số bị thiếu, sai kiểu dữ liệu, hoặc điều hướng đến màn hình không tồn tại đều có thể gây crash. React Navigation 7.x cung cấp hệ thống kiểu toàn diện để giải quyết triệt để vấn đề này.
Bước đầu tiên là định nghĩa param list cho từng navigator:
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;
};Mỗi key trong RootStackParamList tương ứng với một tên màn hình, và value là kiểu của tham số mà màn hình đó nhận. Màn hình Home có params là undefined, nghĩa là không nhận tham số nào. Màn hình ArticleDetail yêu cầu hai tham số bắt buộc: articleId kiểu string và source là union type 'feed' | 'search'.
Tiếp theo, mỗi screen component sử dụng type NativeStackScreenProps để nhận được type-safe props:
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} />
);
}Điểm mạnh của mẫu thiết kế này nằm ở tính toàn vẹn end-to-end. Khi một màn hình điều hướng đến ArticleDetail, TypeScript bắt buộc phải cung cấp đúng articleId và source. Bên trong ArticleDetailScreen, route.params được định kiểu chính xác mà không cần bất kỳ kiểm tra runtime nào. Nếu sau này cần thêm hoặc bớt tham số, TypeScript sẽ báo lỗi tại tất cả các vị trí liên quan trong toàn bộ mã nguồn.
Với các component không phải là screen children trực tiếp, hãy sử dụng useNavigation<NativeStackNavigationProp<RootStackParamList>>() để đạt được type safety tương tự mà không cần prop drilling.
Xây dựng TurboModule Type-Safe với Codegen
TurboModules là cơ chế chính để truy cập API native từ JavaScript trong kiến trúc mới của React Native. Khác với Native Modules cũ sử dụng bridge dựa trên JSON, TurboModules sử dụng JSI (JavaScript Interface) để giao tiếp trực tiếp giữa JavaScript và mã native, mang lại hiệu suất vượt trội.
Điều làm cho TurboModules đặc biệt liên quan đến type safety là vai trò của Codegen. Nhà phát triển viết spec module bằng TypeScript, và Codegen tự động sinh ra mã native (Java/Kotlin cho Android, Objective-C/C++ cho iOS) đảm bảo hợp đồng kiểu được tuân thủ ở cả hai phía.
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');Interface Spec định nghĩa chính xác các phương thức mà module native phải triển khai: getDeviceModel() trả về string đồng bộ, getBatteryLevel() trả về Promise<number>, getStorageInfo() trả về Promise với object có cấu trúc cụ thể gồm ba trường số. Codegen đọc file này và sinh ra abstract class NativeDeviceInfoSpec ở phía native.
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)
}
}Mô hình này tạo ra type safety xuyên suốt từ JavaScript đến mã native. Nếu TypeScript spec định nghĩa getBatteryLevel() trả về Promise<number>, thì Kotlin implementation bắt buộc phải trả về Promise<Double>. Bất kỳ sai lệch nào đều bị phát hiện tại thời điểm biên dịch, không phải khi người dùng đang sử dụng ứng dụng.
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
Trong các ứng dụng React Native thực tế, data fetching chiếm phần lớn logic nghiệp vụ. Việc tạo custom hook generic kết hợp TanStack Query cho phép tái sử dụng mã nguồn mà vẫn đảm bảo type safety tuyệt đối:
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 numberHook useApiQuery sử dụng generic type parameter T để truyền kiểu dữ liệu xuyên suốt từ lời gọi đến kết quả trả về. Khi gọi useApiQuery<Article[]>(...), TypeScript tự động suy luận rằng data.data có kiểu Article[] và data.meta.totalPages có kiểu number. Mọi truy cập sai tên trường hoặc sai kiểu đều được cảnh báo ngay trong trình soạn thảo.
Pattern Omit<UseQueryOptions<ApiResponse<T>>, 'queryKey' | 'queryFn'> cho phép người dùng hook truyền thêm các tùy chọn của TanStack Query như staleTime, enabled hay refetchInterval mà không ghi đè queryKey và queryFn đã được hook xử lý sẵn.
Discriminated Unions cho State Machines
Quản lý trạng thái giao diện người dùng là một trong những bài toán phức tạp nhất trong phát triển ứng dụng mobile. Một màn hình có thể ở trạng thái đang tải, lỗi, trống hoặc đã có dữ liệu, và mỗi trạng thái cần hiển thị giao diện khác nhau. Discriminated unions trong TypeScript cung cấp giải pháp an toàn và hiệu quả cho bài toán này:
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 discriminated union hoạt động dựa trên thuộc tính chung status (được gọi là discriminant). Khi TypeScript gặp case 'error':, nó tự động thu hẹp (narrow) kiểu của state thành { status: 'error'; error: string; retryCount: number }. Bên trong mỗi case, chỉ các thuộc tính thuộc về trạng thái đó mới có thể truy cập được.
Lợi ích lớn nhất là tính exhaustive checking. Nếu sau này bổ sung thêm trạng thái mới vào union type mà không xử lý trong câu lệnh switch, TypeScript sẽ báo lỗi biên dịch. Điều này đảm bảo không bao giờ bỏ sót bất kỳ trạng thái nào trong logic hiển thị giao diện.
Anti-pattern phổ biến: { isLoading: boolean; error?: string; data?: T }. Kiểu này cho phép tồn tại trạng thái như { isLoading: true, error: 'fail', data: [...] } — ba tín hiệu mâu thuẫn cùng lúc. Discriminated unions ngăn chặn điều này ở cấp độ type system.
Câu hỏi phỏng vấn React Native TypeScript
Dưới đây là năm câu hỏi phỏng vấn phổ biến nhất về React Native và TypeScript trong năm 2026, cùng gợi ý trả lời chi tiết.
Codegen đảm bảo type safety giữa JavaScript và mã native như thế nào?
Codegen đọc các file TypeScript (hoặc Flow) spec và sinh ra mã interface cho C++, Objective-C++ và Java/Kotlin. Các native interface được sinh ra bắt buộc chữ ký phương thức, kiểu tham số và kiểu trả về phải khớp chính xác với định nghĩa trong spec. Nếu native implementation sai lệch, chẳng hạn trả về Int khi spec khai báo Double hoặc thiếu trường trong struct, native compiler sẽ từ chối build. Cơ chế này chuyển lỗi kiểu từ runtime crash sang build-time failure.
Strict TypeScript API là gì và tại sao nó quan trọng?
Được giới thiệu từ React Native 0.80, Strict TypeScript API sinh types trực tiếp từ mã nguồn của React Native thay vì duy trì các file .d.ts viết tay. Nó giới hạn imports chỉ đến root package react-native, loại bỏ deep imports. Điều này xác định một API surface ổn định — các thao tác refactoring nội bộ không thể làm hỏng mã của consumer nếu consumer chỉ sử dụng strict types. Kích hoạt bằng cách thêm "types": ["react-native/types/strict"] vào tsconfig.json.
Làm thế nào để đảm bảo type safety cho navigation params trong deep linking?
React Navigation 7 cho phép khai báo linking config cùng kiểu với RootStackParamList. Khi định nghĩa linking.config.screens, TypeScript kiểm tra rằng mỗi tên màn hình và hàm parse cho params đều khớp với type đã khai báo. Đối với deep link URL như /article/123?source=feed, hàm parse phải trả về object đúng kiểu { articleId: string; source: 'feed' | 'search' }. Kết hợp với Zod schema validation cho runtime, hệ thống đạt được type safety ở cả hai cấp độ: biên dịch và thực thi.
Giải thích cách discriminated unions cải thiện state management trong React Native.
Discriminated unions mô hình hóa mỗi trạng thái có thể có của một màn hình dưới dạng union type với thuộc tính discriminant. TypeScript tự động narrow kiểu dựa trên discriminant, đảm bảo mã nguồn chỉ truy cập các thuộc tính hợp lệ cho từng trạng thái. Quan trọng hơn, TypeScript kiểm tra tính đầy đủ (exhaustiveness) — nếu thêm trạng thái mới mà không xử lý trong switch, trình biên dịch sẽ báo lỗi. Pattern này loại bỏ hoàn toàn các lỗi "impossible state" vốn rất phổ biến khi sử dụng nhiều biến boolean riêng lẻ.
TurboModules khác gì so với hệ thống Native Modules cũ?
Native Modules giao tiếp qua async bridge, serialize toàn bộ dữ liệu thành JSON. TurboModules sử dụng JSI (JavaScript Interface) cho các lời gọi C++ trực tiếp và đồng bộ, không có serialization overhead. TurboModules cũng được tải lazy (chỉ khi sử dụng lần đầu thay vì lúc khởi động ứng dụng, giảm thời gian cold start) và sử dụng Codegen để sinh interface type-safe từ TypeScript specs. Hệ thống cũ dựa vào việc parse ReadableMap / NSDictionary với type coercion tại runtime; TurboModules bắt buộc kiểu tại thời điểm biên dịch.
Để tìm hiểu thêm về câu hỏi phỏng vấn React Native, phần native modules chuyên sâu bao gồm JSI, Fabric và kiến trúc bridgeless chi tiết.
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.
Kết luận
React Native kết hợp TypeScript trong năm 2026 không chỉ là xu hướng mà đã trở thành nền tảng kỹ thuật bắt buộc cho các dự án mobile chuyên nghiệp. Dưới đây là tóm tắt các điểm quan trọng:
- Strict TypeScript API (0.80+) giới hạn imports vào API surface ổn định, ngăn chặn lỗi phát sinh từ các thay đổi nội bộ của React Native. Nên kích hoạt cho mọi dự án mới
- Codegen sinh native interface từ TypeScript spec, chuyển lỗi kiểu từ runtime crash sang build-time failure xuyên suốt ranh giới JS-native
- Typed navigation params thông qua
RootStackParamListvàNativeStackScreenPropsphát hiện tên màn hình sai hoặc thiếu tham số trước khi ứng dụng chạy - Discriminated unions mô hình hóa trạng thái màn hình thành các nhánh loại trừ lẫn nhau, loại bỏ hoàn toàn các "impossible state" ở cấp độ type system
- TurboModules với typed specs thay thế hệ thống parse
ReadableMap/NSDictionarycũ, đảm bảo type safety đầy đủ từ JavaScript qua C++ đến mã native của từng nền tảng - Generic API hooks với TanStack Query bảo toàn suy luận kiểu từ endpoint đến component mà không cần ép kiểu 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ẻ
Chia sẻ
Bài viết liên quan

React Native 0.85 nam 2026: Animation Backend moi, Strict TypeScript API va cau hoi phong van
React Native 0.85 gioi thieu Shared Animation Backend, kien truc post-bridge va Metro TLS. Phan tich chi tiet voi vi du code va cau hoi phong van.

Kiến Trúc Mới React Native 2026: Hermes V1, Fabric, TurboModules và Bridgeless Mode
Phân tích chuyên sâu về Kiến Trúc Mới React Native trong năm 2026, bao gồm Hermes V1, Fabric Renderer, TurboModules và chế độ Bridgeless. Bài viết cung cấp các ví dụ code thực tế và câu hỏi phỏng vấn phổ biến.

Expo Router trong React Native: Hướng dẫn đầy đủ về định tuyến dựa trên file
Expo Router mang hệ thống định tuyến dựa trên file đến React Native, lấy cảm hứng từ Next.js. Hướng dẫn toàn diện với cấu hình, tab navigation, dynamic routes, modal, middleware và bảo vệ route.