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
相关产品推荐
相关产品推荐

