React Hook中TypeScript Pick类型无声明报错的原因及排查方法
React TypeScript 类型检查差异问题解析
示例代码
import { useMemo, useState } from "react"; import "./styles.css"; export default function App() { type TTest = { t1: string; t2: string; }; // 此处无报错 const [testState] = useState<Pick<TTest, "t1">>(() => ({ t1: "testState1", t2: "2", t3: "3" })); // 此处也无报错 const testMemo = useMemo<Pick<TTest, "t1">>( () => ({ t1: "testMemo1", t2: "2", t3: "3" }), [] ); // 此处t2键报错 const testConst: Pick<TTest, "t1"> = { t1: "testConst1", t2: "2", t3: "3" }; return ( <div className="App"> {testState.t1}/{testState.t2}/{testState.t3} <br /> {testMemo.t1}/{testMemo.t2}/{testMemo.t3} <br /> {testConst.t1}/{testConst.t2}/{testConst.t3} </div> ); }
问题描述
我定义了三个变量,分别通过useState、useMemo声明,以及直接定义对象,三者均使用Pick<TTest, "t1">类型。但useState和useMemo的声明阶段不会报错(仅在访问t2、t3属性时才会报错),而直接赋值的对象会立即报错。将Pick替换为Omit后问题依然存在。请问这是什么原因?如何在声明阶段就能检测到这种类型不匹配?
原因分析
核心差异来自TypeScript的两种类型检查逻辑:
- 直接变量赋值:当给变量直接赋值对象字面量时,TypeScript会触发多余属性检查(excess property checking)——只要对象包含目标类型未定义的属性,就会立即抛出错误,这是字面量赋值的严格校验规则。
- React钩子回调返回值:
useState、useMemo这类钩子的泛型参数采用的是结构兼容性校验——只要返回的对象包含目标类型的所有必填属性,就会被判定为符合类型要求,不会主动检查多余属性,直到你访问这些未被类型定义的属性时,才会触发类型错误。
解决办法
要在声明阶段就检测到这类多余属性的问题,可以采用以下两种方案:
1. 显式断言回调返回值类型
在钩子的回调函数中,给返回的对象加上类型断言,强制TypeScript触发多余属性检查:
// useState 改造示例 const [testState] = useState<Pick<TTest, "t1">>( () => ({ t1: "testState1", t2: "2", // 此处立即报错 t3: "3" } as Pick<TTest, "t1">) ); // useMemo 改造示例 const testMemo = useMemo<Pick<TTest, "t1">>( () => ({ t1: "testMemo1", t2: "2", // 此处立即报错 t3: "3" } as Pick<TTest, "t1">), [] );
2. 封装严格类型校验工具函数
如果需要多次使用,可以封装一个工具函数,通过参数的字面量校验强制检查多余属性:
function strictAssert<T>(value: T): T { return value; } // 使用示例 const [testState] = useState<Pick<TTest, "t1">>( () => strictAssert<Pick<TTest, "t1">>({ t1: "testState1", t2: "2", // 此处立即报错 t3: "3" }) );
这个函数会让TypeScript将传入的对象视为字面量,从而触发多余属性检查,确保对象完全匹配目标类型。
内容的提问来源于stack exchange,提问作者nato nato
相关产品推荐
相关产品推荐

