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

两个不同useState setter函数联合类型报错问题求助

问题描述

我有两个React组件Apartment和House,它们的状态都包含共同的pictures属性(字符串数组类型)。我编写了FileUploader组件,希望接收父组件的useState setter函数来更新pictures数组。但在为FileUploader的setFunction属性定义联合类型React.Dispatch<React.SetStateAction<HouseType> | React.SetStateAction<ApartmentType>>时,TypeScript抛出类型不兼容错误:

Type 'Dispatch<SetStateAction<HouseType>>' is not assignable to type 'Dispatch<SetStateAction<ApartmentType> | SetStateAction<HouseType>>'.
  Type 'SetStateAction<ApartmentType> | SetStateAction<HouseType>' is not assignable to type 'SetStateAction<HouseType>'.
    Type 'ApartmentType' is not assignable to type 'SetStateAction<HouseType>'.
      Property 'owner' is missing in type 'ApartmentType' but required in type 'HouseType'

原以为联合类型能覆盖两种情况,但实际不行,请问哪里出错了?相关代码如下:

Apartment.ts

type ApartmentType = {
    landlord: string;
    pictures: string[];
}

function Apartment () {
    const [ apartmentData, setApartmentData ] = useState<ApartmentType>({
        landlord: 'juan',
        pictures: [ 'url1', 'url2' ]
    })

    return (
        <h1>Apartment</h1>
        <FileUploader setFunction={setApartmentData} />
    )
}

House.ts

type HouseType = {
    owner: string;
    pictures: string[];
}

function House () {
    const [ houseData, setHouseData ] = useState<HouseType>({
        owner: 'Jhon',
        pictures: [ 'url1', 'url2' ]
    })

    return (
        <h1>House</h1>
        <FileUploader setFunction={setHouseData} />
    )
}

FileUploader.ts

type FileUploaderProps = {
    setFunction: React.Dispatch<React.SetStateAction<HouseType> | React.SetStateAction<ApartmentType>>
}

function FileUploader ({ setFunction }: FileUploaderProps) {
    function updatePictures () {
        setFunction((prevValue: any) => ({ ...prevValue, pictures: [ ...prevValue.pictures, 'newUrl1', 'newUrl2'] }))
    }

    return (
        <div>
            <h1>File Uploader</h1>
            <button type='button' onClick={updatePictures}></button>
        </div>
    )
}

错误原因

问题出在TypeScript中函数参数的逆变特性:如果函数A能接受类型B的参数,函数B能接受类型A的参数(且A是B的子类型),那么函数A是函数B的子类型。

你定义的setFunction类型是Dispatch<SetStateAction<HouseType> | SetStateAction<ApartmentType>>,意味着这个函数必须能接受HouseType或ApartmentType任意一种类型的更新操作。但父组件的setHouseData是Dispatch<SetStateAction<HouseType>>,它只能处理HouseType相关的更新,无法接受ApartmentType的输入——如果FileUploader用ApartmentType调用setHouseData,会直接导致类型错误,所以TypeScript拒绝了这个赋值。


解决方案

方案1:泛型约束(推荐)

让FileUploader成为泛型组件,约束泛型类型必须包含pictures属性,TypeScript会自动匹配父组件的状态类型:

// 定义共同属性的约束类型
type HasPictures = {
  pictures: string[];
};

// 泛型Props类型
type FileUploaderProps<T extends HasPictures> = {
  setFunction: React.Dispatch<React.SetStateAction<T>>;
};

// 泛型组件
function FileUploader<T extends HasPictures>({ setFunction }: FileUploaderProps<T>) {
  function updatePictures() {
    setFunction((prevValue) => ({
      ...prevValue,
      pictures: [...prevValue.pictures, 'newUrl1', 'newUrl2']
    }));
  }

  return (
    <div>
      <h1>File Uploader</h1>
      <button type='button' onClick={updatePictures}>添加图片</button>
    </div>
  );
}

父组件使用时无需额外指定类型,TypeScript会自动推断:

// Apartment组件中
<FileUploader setFunction={setApartmentData} />
// House组件中
<FileUploader setFunction={setHouseData} />

方案2:调整联合类型的结构

如果不想用泛型,可以将setFunction定义为两个Dispatch类型的联合(而非Dispatch包裹联合类型):

type FileUploaderProps = {
  setFunction: React.Dispatch<React.SetStateAction<HouseType>> | React.Dispatch<React.SetStateAction<ApartmentType>>;
};

此时updatePictures中的prevValue会被推断为HouseType | ApartmentType,由于我们只操作共同的pictures属性,TypeScript会允许这个操作。

方案3:解耦组件职责

把图片更新逻辑交给父组件处理,FileUploader只负责通知父组件添加新图片,彻底解耦状态结构:

type FileUploaderProps = {
  onAddPictures: (newPictures: string[]) => void;
};

function FileUploader({ onAddPictures }: FileUploaderProps) {
  function updatePictures() {
    onAddPictures(['newUrl1', 'newUrl2']);
  }

  return (
    <div>
      <h1>File Uploader</h1>
      <button type='button' onClick={updatePictures}>添加图片</button>
    </div>
  );
}

父组件中使用:

// Apartment组件
<FileUploader onAddPictures={(newPics) => setApartmentData(prev => ({...prev, pictures: [...prev.pictures, ...newPics]}))} />
// House组件
<FileUploader onAddPictures={(newPics) => setHouseData(prev => ({...prev, pictures: [...prev.pictures, ...newPics]}))} />

内容的提问来源于stack exchange,提问作者GhostOrder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 00:01:01