为何TypeScript不对useState中超集类型赋值子集类型报错?
问题解答
为什么TypeScript没有报错?
TypeScript采用结构类型系统,判断类型兼容性的核心是「值的结构是否满足目标类型的所有要求」。你的Animal类型包含了Dog的全部必填属性(name、food),虽然多了legs属性,但Dog并没有明确禁止额外属性,因此TypeScript认为Animal类型的值可以安全赋值给Dog类型,这是预期行为。
注意:额外属性检查仅在对象字面量直接赋值时触发(比如const d: Dog = {name: '', food: '', legs: 4}会报错),但对于已标注类型的变量(比如你的animal),TypeScript只会校验结构兼容性,不会触发额外属性检查。
如何让TypeScript正确报错?
要让超集类型无法赋值给Dog,需要让Dog成为严格类型,禁止任何未声明的额外属性,以下是两种常用方案:
方案1:给Dog添加索引签名
通过[key: string]: never明确禁止所有未定义的额外属性:
interface Animal { name: string; food: string; legs: number; } interface Dog { name: string; food: string; [key: string]: never; // 禁止所有未声明的额外属性 } const animal: Animal = { name: 'animal', food: 'animal food', legs: 4, }; function App() { // 此时TypeScript会报错:类型“Animal”不能赋值给类型“Dog”。类型“Animal”中的属性“legs”与索引签名不兼容。 const [data, setData] = useState<Dog>(animal); }
方案2:使用类型守卫做严格校验
如果不想修改Dog接口定义,可以编写类型守卫函数,同时实现编译时提示和运行时校验:
interface Animal { name: string; food: string; legs: number; } interface Dog { name: string; food: string; } const animal: Animal = { name: 'animal', food: 'animal food', legs: 4, }; // 类型守卫函数:断言值严格匹配Dog类型 function assertIsDog(value: unknown): asserts value is Dog { const candidate = value as Dog; // 检查必填属性类型 if (typeof candidate.name !== 'string' || typeof candidate.food !== 'string') { throw new Error('不符合Dog类型结构'); } // 检查是否存在额外属性 const allowedKeys = new Set(['name', 'food']); const extraKeys = Object.keys(candidate).filter(key => !allowedKeys.has(key)); if (extraKeys.length > 0) { throw new Error(`存在额外属性:${extraKeys.join(', ')}`); } } function App() { // 用函数式初始值触发校验 const [data, setData] = useState<Dog>(() => { assertIsDog(animal); // 运行时会抛出错误,结合方案1可实现编译时报错 return animal; }); }
内容的提问来源于stack exchange,提问作者JoeZHH
相关产品推荐
相关产品推荐

