You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React前端结合TypeScript处理多类型Journal数据并实现类型安全

如何在React前端结合TypeScript处理多类型Journal数据并实现类型安全

嘿,这个场景我太熟悉了!结合TypeScript和React处理多类型的Journal数据,核心是利用TS的类型系统做约束,同时让组件能自动识别不同类型的数据。我来一步步给你捋清楚怎么做:

1. 先搞定共享的类型定义

既然后端已经用TS定义了30种Journal的接口,最好把这些类型抽成一个共享的类型文件(比如types.ts),前后端共用。这里关键是用判别式联合类型,给每个Journal类型加一个唯一的type字段作为标识,这样TS能自动根据这个字段推断具体类型:

// types.ts - 前后端共用
// 先定义基础的公共字段
interface BaseJournal {
  id: string;
  createdAt: string;
  // 其他所有Journal都有的字段
}

// 每个具体的Journal类型继承BaseJournal,并指定唯一的type值
interface DailyJournal extends BaseJournal {
  type: "daily"; // 固定值,作为判别符
  dailyLog: string;
  moodRating: number;
}

interface WeeklyJournal extends BaseJournal {
  type: "weekly";
  weeklyGoals: string[];
  progressSummary: string;
}

// 把所有Journal类型合并成一个联合类型
type Journal = DailyJournal | WeeklyJournal | /* 剩下28种类型 */;

2. 给自定义Hook加上类型安全

你的自定义Hook负责从后端拿数据,直接让它返回我们定义好的Journal类型数组就行。这里有两个关键点:给状态指定类型,以及处理后端返回数据的类型校验:

// useJournalData.ts
import { useState, useEffect } from 'react';
import { Journal } from './types';

export const useJournalData = () => {
  // 直接给state指定Journal[]类型,初始值是空数组
  const [journals, setJournals] = useState<Journal[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchJournals = async () => {
      try {
        const res = await fetch('/api/journals');
        // 这里先用类型断言告诉TS返回的是Journal数组
        // 如果担心后端数据不符合类型,后面可以加运行时校验
        const fetchedData = await res.json() as Journal[];
        setJournals(fetchedData);
      } catch (err) {
        console.error('获取Journal数据失败:', err);
      } finally {
        setLoading(false);
      }
    };

    fetchJournals();
  }, []);

  return { journals, loading };
};

3. 让Journal组件的状态天生带类型

因为你的自定义Hook已经返回了类型化的journals,所以在Journal组件里直接用就行,不需要额外处理状态的类型——TS会自动推导:

// Journal.tsx
import { useJournalData } from './useJournalData';
import DailyJournalRow from './DailyJournalRow';
import WeeklyJournalRow from './WeeklyJournalRow';
// 导入其他28种Row组件...

const Journal = () => {
  const { journals, loading } = useJournalData();

  if (loading) {
    return <div>加载中...</div>;
  }

  return (
    <div className="journal-list">
      {journals.map(journal => {
        // 这里TS会根据journal.type自动推断具体类型
        switch (journal.type) {
          case "daily":
            return <DailyJournalRow key={journal.id} journal={journal} />;
          case "weekly":
            return <WeeklyJournalRow key={journal.id} journal={journal} />;
          // 依次处理剩下的28种类型...
          default:
            // 加一个兜底检查,确保所有类型都被覆盖
            // 如果新增了类型但没处理,TS会直接报错
            const _unhandledType: never = journal;
            return <div>未知的Journal类型: {journal.type}</div>;
        }
      })}
    </div>
  );
};

export default Journal;

4. 给行组件加上严格的类型约束

每个行组件只接受对应的Journal类型,这样在组件内部访问字段时完全是类型安全的,TS会自动提示可用字段,也不会出现访问不存在字段的错误:

// DailyJournalRow.tsx
import { DailyJournal } from './types';

// 组件Props只接受DailyJournal类型
interface DailyJournalRowProps {
  journal: DailyJournal;
}

const DailyJournalRow = ({ journal }: DailyJournalRowProps) => {
  return (
    <div className="journal-row">
      <h4>每日日志 #{journal.id}</h4>
      <p>心情评分: {journal.moodRating}</p>
      <p>{journal.dailyLog}</p>
      {/* 这里访问journal的字段都是安全的,TS会阻止你访问不存在的字段 */}
    </div>
  );
};

export default DailyJournalRow;

可选:加运行时校验更稳妥

如果担心后端返回的数据不符合TS类型(比如字段缺失、类型错误),可以用zod这类库做运行时校验,同时还能自动生成TS类型:

// 用zod重构types.ts
import { z } from 'zod';

const BaseJournalSchema = z.object({
  id: z.string(),
  createdAt: z.string(),
});

const DailyJournalSchema = BaseJournalSchema.extend({
  type: z.literal("daily"),
  dailyLog: z.string(),
  moodRating: z.number(),
});

const WeeklyJournalSchema = BaseJournalSchema.extend({
  type: z.literal("weekly"),
  weeklyGoals: z.array(z.string()),
  progressSummary: z.string(),
});

const JournalSchema = z.union([DailyJournalSchema, WeeklyJournalSchema]);
// 自动生成TS类型
type Journal = z.infer<typeof JournalSchema>;

// 在Hook里用schema解析数据
const fetchedData = JournalSchema.array().parse(await res.json());

这样如果后端返回的数据不符合定义,会直接抛出错误,避免脏数据流入组件。

备注:内容来源于stack exchange,提问作者Andrew M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:19:27