React Native と TypeScript 2026年版:型安全なアーキテクチャと面接対策

TypeScriptを活用した型安全なReact Nativeアプリを構築する方法を解説。Codegen、TurboModules、0.87で必須となったStrict TypeScript API、アーキテクチャパターン、型付きナビゲーション、ツールチェーン要件、面接質問を網羅する。

React Native TypeScriptの型安全アーキテクチャとコード例、モバイルデバイス

2026年半ばの時点で、React NativeにおけるTypeScriptの型安全性は大きく成熟している。バージョン0.87ではStrict TypeScript APIが必須のデフォルトとなり、大規模なツールチェーンの近代化が完了した。TypeScriptは、JavaScriptとネイティブコードをつなぐ単なるオプションのツールではなくなった。コンポーネントのpropsからTurboModuleのインターフェースに至るまで、アプリケーション全体の契約を駆動し、本番環境のクラッシュログではなくビルド時に不整合を検出する仕組みとなっている。

0.87での変更点

React Native 0.87(2026年8月)では、Strict TypeScript APIが必須のデフォルトとなった。また、Node.js 22.13以上、Kotlin 2.0以上、Android Gradle Plugin 9が最低要件として設定された。レガシーのディープインポートに対するオプトアウトは0.88までのみ利用可能である。

TypeScriptがReact Nativeのデフォルトになった理由

バージョン0.76以降、npx react-native initコマンドで生成されるプロジェクトはすべてTypeScriptで構成されている。しかし、本質的な変化はネイティブ境界で起きた。Codegen導入以前、開発者はJavaScriptからObjective-CやKotlinへの境界を越える際に手動の型アサーションを記述していた。これは文字列ベースの暗黙的な契約であり、実行時に無言で破綻するものだった。CodegenはTypeScriptの仕様ファイルを読み取り、C++、Objective-C++、Java/Kotlinのインターフェースを自動生成する。TypeScriptの仕様がnumberを返すメソッドを宣言すれば、生成されたネイティブインターフェースがその制約をコンパイル時に強制する。

React Nativeの公式ドキュメントでは基本的なセットアップが解説されているが、プロダクション環境で重要な型安全パターンはさらに先を行く。型付きナビゲーションスタック、ジェネリックAPIフック、状態マシン用のタグ付きユニオン、そしてCodegen駆動のTurboModule仕様がその中核を成す。

Strict TypeScript API:オプトインからデフォルトへ

Strict TypeScript APIはReact Native 0.80でオプトイン機能として導入された。0.87では、すべての新規プロジェクトで必須のデフォルトとなっている。型は手動でメンテナンスされるのではなく、React Nativeのソースコードから直接生成されるため、ドキュメント化されたAPIと実際の実装の間のズレが解消される。

tsconfig.json(0.87以降のデフォルト設定)json
{
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

この設定により、react-native/Libraries/Text/Textのようなサブパスからのインポートは型エラーとなる。すべてのインポートはルートのreact-nativeパッケージから行う必要がある。ライブラリや古いコードベースがディープインポートに依存している場合、0.88まで一時的なオプトアウトが利用可能である。

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

このオプトアウトは0.89で削除される。それまでに移行を完了する必要がある。

0.87のツールチェーン要件

React Native 0.87では、ビルドチェーン全体の最低バージョンが引き上げられた。

コンポーネント最低要件
Node.js22.13.0
Kotlin2.0(バンドル版: 2.2.0)
Android Gradle Plugin9.x
Android compileSdk34(最小)、37(ターゲット)

Androidでは、AGP 9に追加の設定が必要となる。

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

これらの要件は0.87以降を対象とするすべてのプロジェクトに適用される。New Architectureマイグレーションガイドでは、これらの変更の広範な背景が解説されている。

React Navigation 7による型安全なナビゲーション

React Navigation 7.xは、TypeScriptのファーストクラスサポートを提供している。基本パターンは、RootStackParamList型を定義して各画面名とそのパラメータをマッピングし、その型をナビゲーターと画面コンポーネントに伝播させるというものである。

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

画面コンポーネントは、手動キャストなしで型付きpropsを受け取る。

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 は string — 型によって保証されている
  // route.params.source は 'feed' | 'search' — ランタイムチェック不要
  const { articleId, source } = route.params;

  // navigation.navigate('Profile', { userId: '123' }) — 型チェック済み
  // navigation.navigate('Profile', {}) — コンパイルエラー: userId が未指定
  return (
    <ArticleView id={articleId} referrer={source} />
  );
}

このパターンにより、ランタイムエラーの一分類が完全に排除される。誤ったパラメータや不足したパラメータで画面へ遷移しようとすると、アプリ実行前にビルド時に失敗する。

useNavigationフックの型付け

直接の画面子コンポーネントではないコンポーネントでは、useNavigation<NativeStackNavigationProp<RootStackParamList>>()を使用することで、propsの受け渡しなしに同等の型安全性を得られる。

Codegenによる型安全なTurboModuleの構築

TurboModulesは、旧来のNative Modulesシステムに取って代わるものである。TypeScriptの仕様ファイルが唯一の信頼できる情報源として機能し、Codegenがそこからネイティブインターフェースを生成する。仕様とネイティブ実装が乖離した場合、ビルドが失敗する。

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

npx react-native codegenを実行すると、対応するC++、Objective-C++、Javaインターフェースが生成される。ネイティブ実装はすべてのメソッドシグネチャと正確に一致する必要がある。例えば、getStorageInfoは3つの数値フィールドを持つオブジェクトを返す必要があり、異なる構造を返すとネイティブ側でコンパイルエラーが発生する。

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

    // 戻り値の型は生成された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)
    }
}

旧アーキテクチャで問題となっていたReadableMapやNSDictionaryのパース処理による暗黙の型変換バグが、このアプローチによって排除される。ネイティブモジュールシステムの詳細については、ネイティブモジュールの面接質問を参照されたい。

React Nativeの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

ジェネリック型を活用した型安全なAPIフック

再利用可能な型付きフックパターンにより、画面間でフェッチロジックを重複させることなく、完全な型推論を維持できる。

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

// 使用例 — T は Article[] として推論される
interface Article {
  id: string;
  title: string;
  publishedAt: string;
}

const { data, isLoading } = useApiQuery<Article[]>(
  ['articles', 'latest'],
  '/articles?sort=latest'
);
// data.data は Article[] — 完全に型付き
// data.meta.totalPages は number

ジェネリックパラメータTは、フックの呼び出し元からクエリ関数、結果を利用するコンポーネントに至るまで、チェーン全体に伝播する。asによるキャストもany型も不要である。

判別共用体による状態マシンの設計

画面の複雑な状態(読み込み中、エラー、空、読み込み完了)は、オプショナルフィールドの集合ではなく、判別共用体としてモデル化するのが最適である。

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 はここでは string — TypeScriptが自動的に絞り込む
      return <ErrorBanner message={state.error} retries={state.retryCount} />;
    case 'empty':
      return <EmptyState message={state.message} />;
    case 'loaded':
      // state.data は T — 完全に型付き
      return renderItem(state.data);
  }
}

このパターンにより、不可能な状態を型レベルで表現不能にできる。loading状態が古いdataを誤って保持することはなく、error状態には常にデバッグのためのコンテキストが含まれる。

部分的な状態オブジェクトの落とし穴

よくあるアンチパターン:{ isLoading: boolean; error?: string; data?: T }。この定義では{ isLoading: true, error: 'fail', data: [...] }のような状態が許容される。3つの矛盾するシグナルが同時に存在する。判別共用体を使えば、このような状態を型レベルで防止できる。

React Native TypeScript 面接質問集

New ArchitectureとTypeScriptが標準となった2026年のシニアモバイルエンジニアリングチームの面接で実際に問われる質問を取り上げる。

CodegenはJavaScript-ネイティブ境界でどのように型安全性を実現していますか?

CodegenはTypeScript(またはFlow)の仕様ファイルを読み取り、C++、Objective-C++、Java/Kotlinのインターフェースコードを生成する。生成されたネイティブインターフェースは、仕様で定義されたメソッドシグネチャ、パラメータ型、戻り値型を正確に強制する。ネイティブ実装が乖離した場合(仕様がDoubleを宣言しているのにIntを返す、構造体からフィールドを省略するなど)ネイティブコンパイラがビルドを拒否する。これにより、型エラーがランタイムクラッシュからビルド時の失敗に移行する。

Strict TypeScript APIとは何か、なぜ重要なのですか?

Strict TypeScript APIは、手書きの.d.tsファイルではなく、React Nativeのソースコードから直接型を生成する。0.87では必須のデフォルトとなった。インポートをルートのreact-nativeパッケージに制限し、ディープインポートを非推奨とする。これにより安定した公開APIサーフェスが定義され、利用者がstrict型のみを使用している限り、内部リファクタリングによって利用者のコードが破壊されることはない。customConditionsによる一時的なオプトアウトは0.88まで利用可能である。

ネストされたナビゲーター間でReact Navigationのパラメータをどのように型付けしますか?

ナビゲーターごとにParamList型を定義し、NavigatorScreenParamsを使用してそれらを合成する。スタック内にネストされたタブナビゲーターの場合、スタックのパラメータリストがタブのパラメータリストを参照する:type RootStack = { Main: NavigatorScreenParams<TabParamList>; Modal: { id: string } }。すべてのnavigate()呼び出しがネスト階層全体を通じて型チェックされ、画面名の誤りやパラメータの欠落がコンパイル時に検出される。

判別共用体はReact Nativeの状態管理でどのような問題を解決しますか?

判別共用体は、相互排他的な状態(loading、error、loaded)をユニオン型の別々の分岐として、statusフィールドをキーにしてモデル化する。TypeScriptはswitch文の各分岐で型を自動的に絞り込むため、state.dataへのアクセスはstate.status === 'loaded'の場合にのみ可能となる。これにより、読み込みインジケーターがエラーデータと同時に表示されるといったバグを防止できる。オプショナルフィールドやbooleanフラグでは、このクラスのバグを防ぐことはできない。

TurboModulesと旧来のNative Modulesシステムの違いを説明してください。

Native Modulesは非同期ブリッジを介して通信し、すべてのデータをJSONにシリアライズしていた。TurboModulesはJSI(JavaScript Interface)を使用して同期的かつ直接的なC++呼び出しを行い、シリアライズのオーバーヘッドがない。また、遅延読み込み(アプリ起動時ではなく初回使用時に読み込む)によりコールドスタート時間を削減し、Codegenを使用してTypeScript仕様から型安全なインターフェースを生成する。旧システムはReadableMap / NSDictionaryのパースによるランタイム型変換に依存していたが、TurboModulesはコンパイル時に型を強制する。

React Native 0.87で削除されたAPIは何ですか?

0.87ではInteractionManager(代わりにrequestIdleCallbackを使用)、NativeMethods / NativeMethodsMixin型(代わりにHostInstanceを使用)、いくつかのStatusBar propsが削除された。src/private/パスへのディープインポートは型エラーとなる。useTurboModulesフラグも削除された。TurboModulesは常に有効となっているためである。

React Nativeの面接質問の完全ガイドでは、アーキテクチャ、パフォーマンス、デバッグのトピックが網羅されている。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

Sources

React Native TypeScript 2026のポイント

  • Strict TypeScript API(0.87で必須化)は安定した公開サーフェスにインポートを制限し、内部変更による破壊を防止する。レガシーのディープインポートに対するオプトアウトは0.88までのみ利用可能である
  • Codegenは TypeScript仕様ファイルからネイティブインターフェースを生成し、JS-ネイティブ境界における型エラーをランタイムクラッシュからビルド時の失敗に移行させる
  • 0.87のツールチェーン最低要件:Node.js 22.13以上、Kotlin 2.0以上、AGP 9、compileSdk 34以上
  • RootStackParamListとNativeStackScreenPropsを使った型付きナビゲーションパラメータにより、画面名の誤りやパラメータの欠落をアプリ実行前に検出できる
  • 判別共用体は画面状態を相互排他的な分岐としてモデル化し、不可能な状態を型レベルで表現不能にする
  • 型付き仕様を持つTurboModulesは、旧来のReadableMap / NSDictionaryパースを置き換え、JavaScriptからC++、プラットフォームネイティブコードまで完全な型安全性を実現する
  • TanStack Queryを使ったジェネリックAPIフックは、エンドポイントからコンポーネントまで手動キャストなしで型推論を維持する

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

今日のチャレンジ

React Native のバグを見つけられますか

実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年8月25日 更新

タグ

#react-native
#typescript
#mobile-development
#new-architecture
#turbomodules

共有

関連記事