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

React测试自定义Hook时出现localStorage相关TS2345类型报错

问题根因

所有报错均为TypeScript类型定义不匹配导致,无逻辑层面问题,对应三类触发场景:

  • 自定义Hook参数类型硬编码为string,实际使用时传入对象类型值,与函数签名不匹配
  • localStorage.getItem()的原生类型定义返回值为string | null(未命中存储项时返回null),直接传入要求入参为string类型的JSON.parse()时触发校验失败
  • 测试用例中冗余为TEST_KEY标注string | null类型,与Hook要求的string类型入参冲突

修复方案

1. 改造自定义Hook,增加泛型支持

原Hook仅支持字符串类型存储值,通过泛型改造可兼容所有可序列化类型(对象、数字、字符串等),同时修正返回值类型避免解构时类型丢失。
修复后代码:

import { useState, useEffect } from 'react';

// 泛型T兼容任意可序列化值类型
export function useStateWithLocalStorage<T>(defaultValue: T, key: string) {
  const [value, setValue] = useState<T>(() => {
    const storedValues = localStorage.getItem(key);
    // 保留原有非空判断逻辑,TS可自动收窄类型
    return storedValues !== null ? (JSON.parse(storedValues) as T) : defaultValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  // 用as const固定返回值为元组类型,解构时value、setValue类型可正确识别
  return [value, setValue] as const;
}

核心改动点:

  • 新增泛型参数<T>,移除defaultValue的string类型限制,匹配实际存储对象/其他类型值的需求
  • 为JSON.parse返回值添加as T类型断言,与state类型对齐
  • 返回值添加as const修饰,避免TS将返回值推断为联合类型数组,保证解构时拿到正确的类型

2. 修正测试用例类型问题

测试用例需要修正三处问题:

  • 移除TEST_KEY上冗余的| null类型标注,key固定为字符串类型
  • 调用JSON.parse处理localStorage.getItem返回值时,测试场景下可使用非空断言!(用例逻辑中已提前触发存储写入,必然能拿到对应值)
  • 泛型改造完成后,传入对象类型的TEST_VALUE不再与Hook参数类型冲突
    修复后测试代码:
import { renderHook, act } from '@testing-library/react';
import { useStateWithLocalStorage } from '../UseStateWithLocalStorage';

describe('Test local storage', () => {
  test('should set local storage with default value', () => {
    const TEST_KEY = 'form';
    const TEST_VALUE = { name: 'matt' };
    renderHook(() => useStateWithLocalStorage(TEST_VALUE, TEST_KEY));
    // 非空断言标记getItem返回值不为null
    expect(JSON.parse(localStorage.getItem(TEST_KEY)!)).toEqual(TEST_VALUE);
  });

  test('should update localStorage when state changes', () => {
    const TEST_KEY = 'form';
    const TEST_VALUE = { name: 'bill' };

    const { result } = renderHook(() => useStateWithLocalStorage(TEST_VALUE, TEST_KEY));

    const [, setValue] = result.current;
    const newValue = { name: 'john' };

    act(() => {
      setValue(newValue);
    });
    // 非空断言标记getItem返回值不为null
    expect(JSON.parse(localStorage.getItem(TEST_KEY)!)).toEqual(newValue);
  });
});

注意事项
  • 非空断言!仅适用于100%确定值不为null/undefined的场景(比如上述测试用例),业务代码中优先使用显式非空判断做类型收窄,示例:
    const storedVal = localStorage.getItem(key);
    if (storedVal) {
      // 此分支内storedVal自动收窄为string类型,可安全传入JSON.parse
      const parsed = JSON.parse(storedVal);
    }
    
  • 泛型改造后的Hook无需额外指定类型,TS会根据传入的defaultValue自动推断存储值的类型,保留完整的类型校验能力。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 09:45:33