为对象添加属性引发TypeScript类型错误的问题求助
TypeScript类型推断问题:添加额外属性后访问对象属性报错
问题场景
我在TypeScript中碰到一个类型错误,具体表现:
- 当
animals对象仅包含carnivores和herbivores属性时,执行animals[current as keyof typeof animals][0].table无类型错误; - 给
animals添加something: "hi"这类字符串类型属性后,上述代码触发错误:
Property 'table' does not exist on type 'string | AnimalCategoryInterface'.
Property 'table' does not exist on type 'string'.
- 硬编码键名
animals["carnivores"][0].table则完全正常。
复现代码
interface AnimalInterface { name: string, weight: number, } interface AnimalCategoryInterface { table: AnimalInterface[], } const wildCarnivores: AnimalCategoryInterface = { table: [ { name: "Lion", weight: 150 }, { name: "Polar Bear", weight: 400 } ] }; const domesticCarnivores: AnimalCategoryInterface = { table: [ { name: "Cat", weight: 4 }, { name: "Dog", weight: 20 } ] }; const wildHerbivores: AnimalCategoryInterface = { table: [ { name: "Deer", weight: 80 }, { name: "Panda", weight: 100 } ] }; const domesticHerbivores: AnimalCategoryInterface = { table: [ { name: "Guinea Pig", weight: 1 }, { name: "Horse", weight: 300 } ] }; let current = "carnivores"; const animals = { something: "hi", carnivores: [wildCarnivores, domesticCarnivores], herbivores: [wildHerbivores, domesticHerbivores] } // 此处触发类型错误 for (let animal of animals[current as keyof typeof animals][0].table) { console.log(`name: ${animal.name}; weight: ${animal.weight}`) } // 此处无错误 for (let animal of animals["carnivores"][0].table) { console.log(`name: ${animal.name}; weight: ${animal.weight}`) }
原因分析
核心问题在于keyof typeof animals的类型范围变化:
- 未添加
something时,keyof typeof animals仅包含"carnivores" | "herbivores",对应属性值均为AnimalCategoryInterface[]类型,TS能确定访问的是合法数组,因此无错误; - 添加
something后,keyof typeof animals扩展为"something" | "carnivores" | "herbivores",此时animals[current as keyof typeof animals]的类型变为string | AnimalCategoryInterface[]——TS无法确定你访问的是字符串还是动物分类数组,字符串类型没有[0].table属性,因此触发错误; - 硬编码
["carnivores"]时,TS能精确推断该键对应的值为AnimalCategoryInterface[],无类型歧义,因此正常。
解决方案
方案1:缩小current的类型范围
直接给current指定合法键的类型,无需断言:
// 定义合法的动物分类键类型 type AnimalCategoryKey = "carnivores" | "herbivores"; // 给current绑定该类型 let current: AnimalCategoryKey = "carnivores"; // 无需断言,TS自动推断合法类型 for (let animal of animals[current][0].table) { console.log(`name: ${animal.name}; weight: ${animal.weight}`) }
方案2:使用类型守卫过滤非法键
若current来源动态(如用户输入),用类型守卫确保访问合法键:
function isAnimalCategoryKey(key: string): key is keyof Omit<typeof animals, "something"> { return key === "carnivores" || key === "herbivores"; } if (isAnimalCategoryKey(current)) { // TS能确定current为合法键,无类型错误 for (let animal of animals[current][0].table) { console.log(`name: ${animal.name}; weight: ${animal.weight}`) } } else { console.error("无效的动物分类键"); }
方案3:从类型中排除非动物分类属性
用Omit工具类型自动过滤不需要的属性,得到合法键类型:
// 排除something属性,生成合法键类型 type AnimalCategoryKey = keyof Omit<typeof animals, "something">; // 断言current为合法键类型 for (let animal of animals[current as AnimalCategoryKey][0].table) { console.log(`name: ${animal.name}; weight: ${animal.weight}`) }
内容的提问来源于stack exchange,提问作者Harry Smith
相关产品推荐
相关产品推荐

