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

React+Supabase项目中TypeScript提示'id属性不存在'该如何解决?

React + Supabase 类型报错问题:Property 'id' does not exist on type 'string'

问题场景

我在React项目中用Supabase作为数据库,通过React状态管理数据。为处理新用户无数据的情况,给educationList状态设置了默认值:

const [educationList, setEducationList] = useState(
    formData?.education ?? [
      {
        id: Math.floor(Math.random() * PROJECT_MAX),
        degree: "",
        university: "",
        startYear: "2023",
        endYear: "2023",
        isCurrentUni: false,
      },
    ]
  );

随后编写了编辑education列表中degree字段的方法:

const editDegree = (id: number, value: string): void => {
    let temp = educationList;
    for (let i = 0; i < temp.length; i++) {
      const element = temp[i];
      if (element.id === id) {
        element.degree = value;
      }
    }

    setEducationList([...temp]);
  };

但TypeScript抛出报错,错误信息如下:

Property 'id' does not exist on type 'string | number | boolean | { [key: string]: Json | undefined; } | Json[]'.
Property 'id' does not exist on type 'string'

Supabase生成的类型定义中,Json是包含对象及多种基础类型的联合类型,为什么会出现这个报错?尤其是为什么会提示string类型不存在id属性?

附Supabase自动生成的类型定义:

export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[];

export interface Database {
  public: {
    Tables: {
      user_profile: {
        Row: {
          byline: string | null;
          details: string | null;
          education: Json[] | null;
          experience: Json[] | null;
          id: number;
          location: string | null;
          name: string | null;
          projects: Json[] | null;
          skills: string[] | null;
          user_id: string | null;
          user_slug: string | null;
        };
        Insert: {
          byline?: string | null;
          details?: string | null;
          education?: Json[] | null;
          experience?: Json[] | null;
          id?: number;
          location?: string | null;
          name?: string | null;
          projects?: Json[] | null;
          skills?: string[] | null;
          user_id?: string | null;
          user_slug?: string | null;
        };
        Update: {
          byline?: string | null;
          details?: string | null;
          education?: Json[] | null;
          experience?: Json[] | null;
          id?: number;
          location?: string | null;
          name?: string | null;
          projects?: Json[] | null;
          skills?: string[] | null;
          user_id?: string | null;
          user_slug?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "user_profile_user_id_fkey";
            columns: ["user_id"];
            isOneToOne: false;
            referencedRelation: "users";
            referencedColumns: ["id"];
          }
        ];
      };
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      [_ in never]: never;
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
}

报错原因

核心问题在于Supabase自动生成的Json是宽泛的联合类型,它覆盖了字符串、数字、布尔值、null、任意键值对对象以及Json数组。而formData?.education的类型是Json[] | null,这意味着TypeScript只能确定数组元素是Json类型的一员,但无法保证它一定是包含id、degree等字段的对象——它可能是字符串、数字甚至嵌套数组。

当你在editDegree中访问element.id时,TypeScript会检查联合类型的所有分支:对于string类型的元素来说,确实不存在id属性,因此抛出这个错误。

另外,虽然你给useState设置了默认的教育对象数组,但TypeScript会优先根据formData?.education的类型(Json[] | null)推断educationList的类型,默认值仅在运行时生效,不会影响类型推断结果。

解决方案

要解决这个问题,需要明确educationList的类型,让TypeScript知道数组元素是我们预期的教育对象,而非宽泛的Json类型。

1. 定义自定义Education类型

先声明符合业务需求的类型:

interface Education {
  id: number;
  degree: string;
  university: string;
  startYear: string;
  endYear: string;
  isCurrentUni: boolean;
}

2. 给useState指定类型并做类型断言

初始化状态时,明确指定状态类型,并将formData?.education断言为自定义类型数组:

const [educationList, setEducationList] = useState<Education[]>(
    (formData?.education as Education[] | undefined) ?? [
      {
        id: Math.floor(Math.random() * PROJECT_MAX),
        degree: "",
        university: "",
        startYear: "2023",
        endYear: "2023",
        isCurrentUni: false,
      },
    ]
  );

这里用as Education[] | undefined告诉TypeScript:我们确认从Supabase获取的education数组就是Education类型的数组,或者为undefined(当formData不存在时)。

3. 优化editDegree方法(可选)

你的editDegree方法直接修改了原数组元素,虽然最后用扩展运算符创建了新数组,但更符合React状态不可变原则的写法是创建新元素对象:

const editDegree = (id: number, value: string): void => {
  setEducationList(prevList => 
    prevList.map(item => 
      item.id === id ? {...item, degree: value} : item
    )
  );
};

这种写法更简洁,也避免了直接修改原状态的潜在问题。

内容的提问来源于stack exchange,提问作者U. Watt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:45:11