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

TypeScript中useReducer结合Context API报类型错误如何解决

问题背景

尝试使用 Context API 配合 useReducer,结合 faker.js 实现商品数据渲染逻辑时,出现两类 TypeScript 类型报错,相关原代码与报错信息如下:

原代码片段

CartReducer 部分代码

import { IProduct } from "../context/Context";

export interface IState {
    products: IProduct;
    cart: [];
}

type Actions =
    | {type:"ADD_TO_CART", payload:number}| {type:"REMOVE_FROM_CART", payload:number}| {type: "CHANGE_CART_QTY", payload: number }

export const cartReducer = (state:IState, action:Actions) =>{
    switch (action.type) {
        case "ADD_TO_CART":
            console.log('ADD_TO_CART')
            break;
        default:
            return state;
    }
}

Context 部分代码

import React, {useContext, useReducer } from "react";
import { faker } from '@faker-js/faker';
import { cartReducer, IState } from "../reducers/CartReducer";

export interface IProduct {
    id: string;
    name: string;
    price: string;
    image: string;
    insTock: number;
    fastdelivery: boolean;
    ratings: number;
    children: React.ReactNode;
}

const CartContext = React.createContext<IProduct | null>(null);

const Context: React.FC <IProduct> = ({ children}) => {

    const products = [...Array(20)].map(() => ({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: faker.commerce.price(),
        image: faker.image.fashion(),
        inStock: faker.helpers.arrayElement([0, 3, 5, 6, 7]),
        fastDelivery: faker.datatype.boolean(),
        ratings: faker.helpers.arrayElement([1, 2, 3, 4, 5]),
    }));

    const initialState: IState = {
        products: products,
        cart: []
    }
    const [state, dispatch] = useReducer(cartReducer, initialState);

    return (
        <CartContext.Provider value={{state, dispatch}}>
            {children}
        </CartContext.Provider>
    )
}

export default Context;

export const CartState = () => {
    return useContext(CartContext)
}

运行报错信息

  • 类型校验错误:生成的products数组缺少IProduct类型要求的id、name、price、image等共计4项以上属性,无法匹配IProduct类型。
Type '{ id: string; name: string; price: string; image: string; inStock: number; fastDelivery: boolean; ratings: number; }[]' is missing the following properties from type 'IProduct': id, name, price, image, and 4 more.
  • useReducer 重载匹配失败:第一类重载提示传入的cartReducer类型为(state: IState, action: Actions) => IState | undefined,无法赋值给ReducerWithoutAction类型;第二类重载提示传入的initialState为IState类型,无法赋值给never类型。
问题原因

一共存在6处类型定义与逻辑错误:

  1. IProduct 接口定义错误:
    • 字段名拼写不一致:接口写的是insTock、fastdelivery,实际faker生成的字段是inStock、fastDelivery
    • 错误加入了children字段:children是Context Provider组件的props属性,不属于商品数据结构
  2. IState 接口定义错误:
    • products字段类型应该是IProduct[](商品数组),原代码写的是单个IProduct类型,和生成的20条商品数组结构不匹配
    • cart字段错误写为[]空元组类型,实际应该是购物车对应的数据数组类型
  3. cartReducer 逻辑缺失返回值:ADD_TO_CART的case分支只打了日志没有return状态,TS推断该分支返回undefined,导致reducer返回类型不匹配
  4. Context 泛型定义错误:原代码createContext的泛型写的是IProduct | null,但实际Provider传入的value是{state, dispatch}结构,类型完全不匹配
  5. Context 组件泛型错误:原代码给React.FC传入的泛型是IProduct,但该组件的props只有children,不需要商品相关字段
  6. 循环依赖问题:CartReducer里从Context引入IProduct,Context里又从CartReducer引入IState,很容易引发类型解析异常
修正方案

1. 统一类型定义,避免循环依赖

调整接口字段,保证类型和实际数据结构一致:

import React, { Dispatch } from "react";

// 商品类型,字段名和faker生成逻辑保持一致,移除不属于商品的children字段
export interface IProduct {
    id: string;
    name: string;
    price: string;
    image: string;
    inStock: number;
    fastDelivery: boolean;
    ratings: number;
    qty?: number;
}

// 全局状态类型
export interface IState {
    products: IProduct[];
    cart: IProduct[];
}

// Action类型
export type Actions =
    | {type:"ADD_TO_CART", payload: IProduct}
    | {type:"REMOVE_FROM_CART", payload: string}
    | {type: "CHANGE_CART_QTY", payload: {id: string, qty: number} }

// Context传入值的类型
export interface ICartContext {
    state: IState;
    dispatch: Dispatch<Actions>
}

2. 修正CartReducer代码

补全所有分支的返回值,保证每个case都返回合法的state:

import { IState, Actions } from "./types";

export const cartReducer = (state:IState, action:Actions): IState =>{
    switch (action.type) {
        case "ADD_TO_CART":
            return {
                ...state,
                cart: [...state.cart, {...action.payload, qty: 1}]
            };
        case "REMOVE_FROM_CART":
            return {
                ...state,
                cart: state.cart.filter(item => item.id !== action.payload)
            };
        case "CHANGE_CART_QTY":
            return {
                ...state,
                cart: state.cart.map(item => item.id === action.payload.id ? {...item, qty: action.payload.qty} : item)
            };
        default:
            return state;
    }
}

3. 修正Context代码

调整Context泛型、组件props类型,匹配实际传入的value结构:

import React, {useContext, useReducer } from "react";
import { faker } from '@faker-js/faker';
import { cartReducer } from "../reducers/CartReducer";
import { IProduct, IState, ICartContext } from "./types";

const CartContext = React.createContext<ICartContext | null>(null);

const Context: React.FC<{children: React.ReactNode}> = ({ children}) => {
    const products: IProduct[] = [...Array(20)].map(() => ({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: faker.commerce.price(),
        image: faker.image.fashion(),
        inStock: faker.helpers.arrayElement([0, 3, 5, 6, 7]),
        fastDelivery: faker.datatype.boolean(),
        ratings: faker.helpers.arrayElement([1, 2, 3, 4, 5]),
    }));

    const initialState: IState = {
        products: products,
        cart: []
    }
    const [state, dispatch] = useReducer(cartReducer, initialState);

    return (
        <CartContext.Provider value={{state, dispatch}}>
            {children}
        </CartContext.Provider>
    )
}

export default Context;

export const CartState = () => {
    const context = useContext(CartContext);
    if (!context) throw new Error("CartState must be used within CartProvider");
    return context;
}

内容的提问来源于stack exchange,提问作者The Dead Man

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:15:33