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

如何在Immer的produce函数中更新泛型类型的嵌套属性?

在React TypeScript中使用Immer处理泛型状态的问题及修复

问题背景

在React TypeScript应用中使用Immer库时,遇到泛型状态更新的类型错误。场景是一个reducer需要处理带有泛型参数的状态,该泛型可支持任意类型(基础类型、数组、对象等),但在produce方法中更新泛型属性时出现类型不兼容报错。

原始代码

import { produce } from 'immer';

type SomeState<TResult> = {
    inner: TResult
}

type SetInner<TResult> = {
    type: 'SET_INNER';
    newValue: TResult;
}

const reducer = <TResult>(
    state: SomeState<TResult>,
    action: SetInner<TResult>
): SomeState<TResult> => {

    return produce(state, draft => {
        switch (action.type) {
            case 'SET_INNER':
                draft.inner = action.newValue;
                break;
        }
    });

}

报错信息

Type 'TResult' is not assignable to type 'Draft'.

报错原因

Immer的Draft<T>类型会对引用类型(如对象、数组)生成代理包装,对基础类型(如字符串、数字)则直接等同于原类型。由于泛型TResult可表示任意类型,TypeScript无法确定action.newValue的类型是否能直接赋值给Draft<TResult>类型的draft.inner,因此抛出类型不兼容错误。

修复方案

使用Immer官方提供的castDraft工具函数,将action.newValue转换为对应的Draft<TResult>类型,即可解决类型不匹配问题。

修复后的代码

import { produce, castDraft } from 'immer';

type SomeState<TResult> = {
    inner: TResult
}

type SetInner<TResult> = {
    type: 'SET_INNER';
    newValue: TResult;
}

const reducer = <TResult>(
    state: SomeState<TResult>,
    action: SetInner<TResult>
): SomeState<TResult> => {

    return produce(state, draft => {
        switch (action.type) {
            case 'SET_INNER':
                draft.inner = castDraft(action.newValue);
                break;
        }
    });

}

如果不想引入额外工具函数,也可以直接使用类型断言强制转换:

draft.inner = action.newValue as Draft<TResult>;

不过castDraft是Immer专门设计的类型转换工具,更符合库的设计规范,推荐优先使用。

验证场景

修复后的代码可完美支持以下使用场景:

基础类型场景

// 基础类型
const initialState = {
    inner: "foo"
};

const action: SetInner<string> = {
    type: 'SET_INNER',
    newValue: "bar"
}

const newState = reducer(initialState, action);
console.log(newState); // 输出: { inner: "bar" }

对象类型场景

// 对象类型
type Point = { x: number, y: number }
const initialState2 = {
    inner: { x: 10, y: 4 }
};

const action2: SetInner<Point> = {
    type: 'SET_INNER',
    newValue: { x: -5, y: 14 }
}

const newState2 = reducer(initialState2, action2);
console.log(newState2); // 输出: { inner: { x: -5, y: 14 } }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 19:35:15