如何解决IDE中关于对象属性的TypeScript类型错误
解决TypeScript遍历StoreUnits对象时的索引类型错误
背景
我用React和MobX写了一个自定义Hook,这个Hook接收StoreUnits类型的对象units,同时用useState保存另一个StoreUnits类型的对象currentUnits。在Hook里我会遍历units对象,如果某个属性和currentUnits的对应属性不一致,就调用setter更新currentUnits。
问题
虽然已经明确units和currentUnits都是StoreUnits类型,但IDE中(运行时无异常,仅IDE报错)的currentUnits[key]和units[key]出现类型错误。
错误信息
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'StoreUnits'. No index signature with a parameter of type 'string' was found on type 'StoreUnits'.ts(7053) const currentUnits: StoreUnits
代码 - 自定义Hook
'use client' import { useState, useEffect } from 'react' import { autorun } from 'mobx' type StoreUnits = { lengthUnit: 0 | 1 | 2 | 3 pointForceUnit: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 stressUnit: 0 | 1 | 2 | 3 areaUnit: 0 | 1 | 2 } const useCurrentUnits = (units: StoreUnits) => { const [currentUnits, setCurrentUnits] = useState<StoreUnits>(units) useEffect(() => { autorun(() => { for (const [key, value] of Object.entries(units)) { if (currentUnits[key] !== units[key]) { setCurrentUnits({...units}) break } } }) }, [units]) return { currentUnits } } export default useCurrentUnits
已尝试方案
我试过用currentUnits[key as 'lengthUnit']来消除错误,但这明显不合理,因为key并不总是'lengthUnit'。
解决方案
问题根源是Object.entries(units)返回的key类型为string,TypeScript无法确认这个字符串就是StoreUnits的合法属性名。以下是几种可行的解决方式:
直接断言key类型
遍历的时候将Object.entries的结果断言为StoreUnits键值对的数组,让TypeScript明确key的合法范围:for (const [key, value] of Object.entries(units) as [keyof StoreUnits, StoreUnits[keyof StoreUnits]][]) { if (currentUnits[key] !== units[key]) { setCurrentUnits({...units}) break } }使用类型守卫
写一个类型守卫函数,严谨校验key是否属于StoreUnits的属性:function isStoreUnitsKey(key: string): key is keyof StoreUnits { return ['lengthUnit', 'pointForceUnit', 'stressUnit', 'areaUnit'].includes(key) } // 遍历中使用类型守卫 for (const [key, value] of Object.entries(units)) { if (isStoreUnitsKey(key)) { if (currentUnits[key] !== units[key]) { setCurrentUnits({...units}) break } } }添加索引签名(不推荐)
给StoreUnits添加字符串索引签名可以绕过错误,但会丢失类型检查的严谨性,不建议使用:type StoreUnits = { lengthUnit: 0 | 1 | 2 | 3 pointForceUnit: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 stressUnit: 0 | 1 | 2 | 3 areaUnit: 0 | 1 | 2 [key: string]: number // 添加索引签名 }
内容的提问来源于stack exchange,提问作者IdoBa
相关产品推荐
相关产品推荐

