# React Native กับ TypeScript ปี 2026: สถาปัตยกรรม Type-Safe และคำถามสัมภาษณ์งาน > สร้างแอป React Native แบบ type-safe ด้วย TypeScript, Codegen, TurboModules และ Strict TypeScript API ครอบคลุมรูปแบบสถาปัตยกรรม, typed navigation และคำถามสัมภาษณ์งานปี 2026 - Published: 2026-06-16 - Updated: 2026-06-16 - Author: SharpSkill - Tags: react-native, typescript, mobile-development, turbomodules, interview - Reading time: 8 min --- การพัฒนา React Native ด้วย TypeScript ในปี 2026 ได้ก้าวเข้าสู่จุดเปลี่ยนสำคัญ เมื่อเวอร์ชัน 0.86 ที่มาพร้อมกับ React 19.1, Strict TypeScript API และสถาปัตยกรรมแบบ bridgeless ที่ขับเคลื่อนด้วย Codegen ได้รับการเผยแพร่อย่างเป็นทางการ TypeScript ไม่ใช่เพียงตัวเลือกเสริมสำหรับเชื่อมต่อระหว่าง JavaScript กับ native code อีกต่อไป แต่ได้กลายเป็นสัญญาหลัก (contract) ที่ควบคุมทุกอย่างตั้งแต่ component props ไปจนถึง TurboModule interfaces ทำให้สามารถตรวจจับข้อผิดพลาดได้ตั้งแต่ขั้นตอน build แทนที่จะรอให้เกิด crash logs ใน production ความเปลี่ยนแปลงนี้ส่งผลกระทบอย่างมากต่อวิธีการออกแบบสถาปัตยกรรมแอปพลิเคชัน รวมถึงมาตรฐานการประเมินผู้สมัครในการสัมภาษณ์งานตำแหน่ง mobile developer > **สิ่งที่เปลี่ยนแปลงในปี 2026** > > React Native 0.80 เปิดตัว Strict TypeScript API แบบ opt-in, เวอร์ชัน 0.82 ลบ legacy bridge อย่างถาวร และเวอร์ชัน 0.85 ลบ interop layer สุดท้ายออกไป ณ เวอร์ชัน 0.86 (มิถุนายน 2026) ทุกโปรเจกต์ใหม่จะเริ่มต้นแบบ fully bridgeless พร้อม TypeScript types ที่ถูก generate โดยตรงจาก source code ## ทำไม TypeScript จึงเป็นค่าเริ่มต้นของโปรเจกต์ React Native ตั้งแต่เวอร์ชัน 0.76 เป็นต้นมา ทุกครั้งที่รันคำสั่ง `npx react-native init` ระบบจะสร้างโปรเจกต์แบบ TypeScript โดยอัตโนมัติ อย่างไรก็ตาม การเปลี่ยนแปลงที่สำคัญจริงเกิดขึ้นที่ขอบเขตระหว่าง JavaScript กับ native code ก่อนที่จะมี Codegen นักพัฒนาต้องเขียน type assertions ด้วยตนเองเมื่อข้อมูลข้ามจาก JavaScript ไปยัง Objective-C หรือ Kotlin ซึ่งเป็นสัญญาแบบ stringly-typed ที่มักเกิดข้อผิดพลาดแบบเงียบในขณะรันไทม์โดยไม่มีสัญญาณเตือน Codegen แก้ปัญหานี้โดยการอ่าน TypeScript spec files แล้ว generate C++, Objective-C++ และ Java/Kotlin interfaces โดยอัตโนมัติ หากไฟล์ spec ประกาศว่า method คืนค่าเป็น `number` interface ที่ถูก generate ฝั่ง native จะบังคับข้อจำกัดนั้นในขั้นตอน compile time ทันที [เอกสาร React Native อย่างเป็นทางการ](https://reactnative.dev/docs/typescript) ครอบคลุมการตั้งค่าเบื้องต้น แต่รูปแบบ type-safe ที่มีความสำคัญใน production นั้นลึกซึ้งกว่า ได้แก่ typed navigation stacks, generic API hooks, discriminated unions สำหรับ state machines และ Codegen-driven TurboModule specs ## การตั้งค่า Strict TypeScript API Strict TypeScript API ที่เปิดตัวใน React Native 0.80 จำกัด public API surface ให้เหลือเฉพาะ types ที่ถูก generate โดยตรงจาก source code ของ React Native เท่านั้น การตั้งค่านี้ป้องกันการพึ่งพา internal modules โดยไม่ตั้งใจ ซึ่งอาจเปลี่ยนแปลงได้ระหว่างเวอร์ชันย่อย ```json // tsconfig.json { "compilerOptions": { "strict": true, "exactOptionalPropertyTypes": true, "noUncheckedIndexedAccess": true, "moduleResolution": "bundler", "jsx": "react-jsx", "types": ["react-native/types/strict"] }, "extends": "@react-native/typescript-config/tsconfig.json" } ``` เมื่อใช้การตั้งค่านี้ การ import จาก subpath เช่น `react-native/Libraries/Text/Text` จะทำให้เกิด type error ทันที ทุก import ต้องมาจาก root `react-native` package เท่านั้น ซึ่งสอดคล้องกับนโยบาย deep-import deprecation ที่บังคับใช้ตั้งแต่เวอร์ชัน 0.80 เป็นต้นมา ตัวเลือก `exactOptionalPropertyTypes` บังคับให้แยกความแตกต่างระหว่าง property ที่เป็น `undefined` กับ property ที่ไม่มีอยู่จริง ส่วน `noUncheckedIndexedAccess` เพิ่ม `| undefined` ให้กับทุก index access ทำให้ต้องตรวจสอบค่าก่อนใช้งาน ทั้งสองตัวเลือกนี้ช่วยลดบั๊กที่เกิดจากสมมติฐานผิดเกี่ยวกับโครงสร้างข้อมูลได้อย่างมีประสิทธิภาพ ## Type-Safe Navigation ด้วย React Navigation 7 [React Navigation 7.x](https://reactnavigation.org/docs/typescript/) มี TypeScript support ระดับ first-class รูปแบบหลักคือการกำหนด `RootStackParamList` type ที่ map ชื่อ screen ทุกหน้าจอเข้ากับ params ที่คาดหวัง จากนั้นส่งผ่าน type นี้ไปยัง navigators และ screen components ทั้งหมด ```typescript // navigation/types.ts 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 จะได้รับ typed props โดยไม่ต้อง cast ด้วยตนเอง ```typescript // screens/ArticleDetailScreen.tsx import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { RootStackParamList } from '../navigation/types'; type Props = NativeStackScreenProps; 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 ( ); } ``` รูปแบบนี้กำจัดข้อผิดพลาดรันไทม์ทั้ง class ออกไป การ navigate ไปยัง screen ด้วย params ที่ผิดหรือขาดหายไปจะล้มเหลวตั้งแต่ขั้นตอน build ไม่ใช่ตอนที่ผู้ใช้งานกำลังใช้แอป ประโยชน์นี้ยิ่งชัดเจนในแอปพลิเคชันขนาดใหญ่ที่มี screen จำนวนมากและ navigation flow ที่ซับซ้อน > **การกำหนด type ให้ useNavigation hook** > > สำหรับ components ที่ไม่ได้เป็น screen children โดยตรง สามารถใช้ `useNavigation>()` เพื่อให้ได้ type safety เท่าเทียมกันโดยไม่ต้อง prop drilling ลงไปในลำดับชั้น ## การสร้าง TurboModule แบบ Type-Safe ด้วย Codegen TurboModules เข้ามาแทนที่ระบบ Native Modules เดิม ไฟล์ TypeScript spec ทำหน้าที่เป็นแหล่งข้อมูลความจริงเพียงแหล่งเดียว (single source of truth) โดย Codegen จะ generate native interfaces จากไฟล์นี้โดยตรง หาก spec กับ native implementation ไม่ตรงกัน build จะล้มเหลวทันที ```typescript // specs/NativeDeviceInfo.ts import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { getDeviceModel(): string; getBatteryLevel(): Promise; getStorageInfo(): Promise<{ totalBytes: number; freeBytes: number; usedPercentage: number; }>; onBatteryChange(callback: (level: number) => void): void; } export default TurboModuleRegistry.getEnforcing('DeviceInfo'); ``` เมื่อรันคำสั่ง `npx react-native codegen` ระบบจะ generate C++, Objective-C++ และ Java interfaces ที่ตรงกัน native implementation ต้อง match ทุก method signature อย่างถูกต้อง ตัวอย่างเช่น `getStorageInfo` ต้องคืนค่า object ที่มี field ตัวเลขสามตัว หากคืนค่าในรูปแบบอื่นจะเกิด compile-time error ฝั่ง native ทันที ```kotlin // android/app/src/main/java/com/app/DeviceInfoModule.kt class DeviceInfoModule(reactContext: ReactApplicationContext) : NativeDeviceInfoSpec(reactContext) { // Return type enforced by generated NativeDeviceInfoSpec override fun getDeviceModel(): String { return Build.MODEL } override fun getBatteryLevel(): Promise { val bm = reactContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager val level = bm.getIntProperty( BatteryManager.BATTERY_PROPERTY_CAPACITY ).toDouble() return Promise.resolve(level) } } ``` แนวทางนี้กำจัดการ parse `ReadableMap` และ `NSDictionary` ที่เคยเป็นสาเหตุของบั๊ก silent type coercion ในสถาปัตยกรรมเดิม ทุกอย่างถูกตรวจสอบตั้งแต่ขั้นตอน compile ทำให้ข้อผิดพลาดที่เกี่ยวกับ type ไม่สามารถหลุดรอดไปถึง production ได้ ## Generic Data Fetching ด้วย Typed API Hooks รูปแบบ typed hook ที่ใช้ซ้ำได้ช่วยหลีกเลี่ยงการเขียน fetch logic ซ้ำในทุก screen พร้อมรักษา type inference อย่างสมบูรณ์ ```typescript // hooks/useApiQuery.ts import { useQuery, UseQueryOptions } from '@tanstack/react-query'; interface ApiResponse { data: T; meta: { page: number; totalPages: number }; } export function useApiQuery( key: readonly string[], endpoint: string, options?: Omit>, 'queryKey' | 'queryFn'> ) { return useQuery>({ queryKey: key, queryFn: async () => { const response = await fetch(`${API_BASE}${endpoint}`); if (!response.ok) throw new ApiError(response.status); return response.json() as Promise>; }, ...options, }); } // Usage — T is inferred as Article[] interface Article { id: string; title: string; publishedAt: string; } const { data, isLoading } = useApiQuery( ['articles', 'latest'], '/articles?sort=latest' ); // data.data is Article[] — fully typed // data.meta.totalPages is number ``` Generic parameter `T` ไหลผ่านทั้งห่วงโซ่อย่างสมบูรณ์ ตั้งแต่จุดเรียก hook ผ่าน query function ไปจนถึง component ที่ใช้ผลลัพธ์ ไม่ต้องใช้ `as` casts และไม่มี `any` types ปรากฏในโค้ด ข้อดีของรูปแบบนี้คือเมื่อเปลี่ยนโครงสร้างข้อมูลของ API (เช่น เพิ่ม field ใหม่หรือเปลี่ยนชนิดข้อมูล) TypeScript จะแจ้ง error ในทุกจุดที่ได้รับผลกระทบทันที ทำให้ refactoring ปลอดภัยและมั่นใจมากขึ้น ## Discriminated Unions สำหรับ State Machines สถานะหน้าจอที่ซับซ้อน เช่น loading, error, empty และ loaded ควรถูกจำลองด้วย discriminated unions แทนที่จะใช้กลุ่มของ optional fields ```typescript // types/screen-state.ts type ScreenState = | { 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(state: ScreenState, renderItem: (data: T) => ReactNode) { switch (state.status) { case 'idle': return null; case 'loading': return ; case 'error': // state.error is string here — TypeScript narrows automatically return ; case 'empty': return ; case 'loaded': // state.data is T — fully typed return renderItem(state.data); } } ``` รูปแบบนี้ทำให้สถานะที่เป็นไปไม่ได้ไม่สามารถแสดงออกในโค้ดได้ (impossible states are unrepresentable) สถานะ `loading` ไม่สามารถแบกข้อมูลเก่า (`data`) ติดไปได้โดยไม่ตั้งใจ และสถานะ `error` จะมีข้อมูลบริบทสำหรับ debug เสมอ TypeScript จะ narrow type โดยอัตโนมัติในแต่ละ branch ของ switch statement ทำให้การเข้าถึง properties ที่ไม่มีอยู่ในสถานะนั้นเกิด compile error ทันที > **หลีกเลี่ยง partial state objects** > > Anti-pattern ที่พบบ่อย: `{ isLoading: boolean; error?: string; data?: T }` รูปแบบนี้อนุญาตให้เกิดสถานะอย่าง `{ isLoading: true, error: 'fail', data: [...] }` ซึ่งมีสัญญาณที่ขัดแย้งกันสามอย่างพร้อมกัน Discriminated unions ป้องกันปัญหานี้ได้ในระดับ type system ## คำถามสัมภาษณ์งาน React Native TypeScript คำถามเหล่านี้สะท้อนสิ่งที่ทีม mobile engineering ระดับ senior ถามในการสัมภาษณ์ปี 2026 เมื่อ New Architecture และ TypeScript กลายเป็นมาตรฐาน **Codegen บังคับ type safety ข้ามขอบเขต JavaScript-native ได้อย่างไร** Codegen อ่าน TypeScript (หรือ Flow) spec files แล้ว generate C++, Objective-C++ และ Java/Kotlin interface code ตัว generated native interfaces บังคับ method signatures, parameter types และ return types ที่ตรงกับ spec อย่างเข้มงวด หาก native implementation เบี่ยงเบน เช่น คืนค่า `Int` ในจุดที่ spec กำหนดเป็น `Double` หรือละเว้น field จาก struct คอมไพเลอร์ฝั่ง native จะปฏิเสธ build ทันที การทำเช่นนี้ย้ายข้อผิดพลาดเกี่ยวกับ type จาก runtime crashes ไปเป็น build-time failures **Strict TypeScript API คืออะไร และทำไมจึงสำคัญ** Strict TypeScript API ที่เปิดตัวใน React Native 0.80 generate types โดยตรงจาก source code ของ React Native แทนที่จะดูแล hand-written `.d.ts` files ระบบจำกัดการ import ให้เฉพาะจาก root `react-native` package เท่านั้น โดย deprecate deep imports ทั้งหมด สิ่งนี้กำหนด public API surface ที่เสถียร ทำให้การ refactor ภายในไม่สามารถทำลายโค้ดของผู้ใช้งานได้ตราบใดที่ใช้ strict types เปิดใช้งานผ่าน `"types": ["react-native/types/strict"]` ใน `tsconfig.json` **จะกำหนด type ให้ React Navigation params ข้าม nested navigators ได้อย่างไร** กำหนด `ParamList` type ต่อ navigator หนึ่งตัว แล้วประกอบเข้าด้วยกันด้วย `NavigatorScreenParams` สำหรับ tab navigator ที่ซ้อนอยู่ภายใน stack param list ของ stack จะอ้างอิงถึง tab's: `type RootStack = { Main: NavigatorScreenParams; Modal: { id: string } }` ทุกการเรียก `navigate()` จะถูก type-checked ผ่านลำดับชั้นทั้งหมด ตรวจจับชื่อ screen ผิดหรือ params ที่ขาดหายไปตั้งแต่ขั้นตอน compile time **Discriminated unions แก้ปัญหาอะไรใน React Native state management** Discriminated unions จำลองสถานะที่เป็น mutually exclusive (loading, error, loaded) เป็น branches แยกของ union type โดยใช้ `status` field เป็นตัวแยก TypeScript narrows type ในแต่ละ branch ของ switch statement ทำให้การเข้าถึง `state.data` เป็นไปได้เฉพาะเมื่อ `state.status === 'loaded'` เท่านั้น รูปแบบนี้ป้องกันสถานะที่เป็นไปไม่ได้ เช่น loading indicator แสดงพร้อมกับ error data ซึ่ง optional fields และ boolean flags ไม่สามารถป้องกันได้ **อธิบายความแตกต่างระหว่าง TurboModules กับระบบ Native Modules เดิม** Native Modules สื่อสารผ่าน async bridge โดย serialize ข้อมูลทั้งหมดเป็น JSON ส่วน TurboModules ใช้ JSI (JavaScript Interface) สำหรับการเรียกแบบ synchronous โดยตรงผ่าน C++ โดยไม่มี serialization overhead นอกจากนี้ TurboModules ยัง load แบบ lazy (โหลดเมื่อใช้งานครั้งแรกแทนที่จะ load ตอนเริ่มแอป ช่วยลดเวลา cold start) และใช้ Codegen generate type-safe interfaces จาก TypeScript specs ระบบเดิมพึ่งพา `ReadableMap` / `NSDictionary` parsing ที่มี runtime type coercion แต่ TurboModules บังคับ types ตั้งแต่ขั้นตอน compile time สำหรับ[คำถามสัมภาษณ์ React Native](/technologies/react-native/interview-questions/rn-native-modules) เพิ่มเติม หัวข้อ native modules deep-dive ครอบคลุม JSI, Fabric และสถาปัตยกรรม bridgeless อย่างละเอียด ## สรุป - Strict TypeScript API (0.80+) จำกัดการ import ให้เฉพาะ public API surface ที่เสถียร ป้องกันการเปลี่ยนแปลงภายในทำลายโค้ด ควรเปิดใช้งานในทุกโปรเจกต์ใหม่ - Codegen generate native interfaces จาก TypeScript spec files ย้ายข้อผิดพลาดเกี่ยวกับ type จาก runtime crashes ไปเป็น build-time failures ข้ามขอบเขต JS-native - Typed navigation params ผ่าน `RootStackParamList` และ `NativeStackScreenProps` ตรวจจับชื่อ screen ผิดและ params ที่ขาดหายไปก่อนที่แอปจะรัน - Discriminated unions จำลองสถานะหน้าจอเป็น branches ที่เป็น mutually exclusive ทำให้สถานะที่เป็นไปไม่ได้ไม่สามารถแสดงออกในระดับ type system - TurboModules ที่มี typed specs แทนที่การ parse `ReadableMap` / `NSDictionary` เดิม บังคับ type safety ตั้งแต่ JavaScript ผ่าน C++ ไปจนถึง platform-native code - Generic API hooks ร่วมกับ [TanStack Query](https://tanstack.com/query/latest) รักษา type inference จาก endpoint ไปจนถึง component โดยไม่ต้อง cast ด้วยตนเอง --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/react-native/react-native-typescript-type-safe-architecture