TypeScript索引类型错误:字符串表达式无法索引指定类型
问题场景
在React组件中通过useState定义basicDetails和errorMessage状态对象后,编写validation函数遍历errorMessage的键时,触发TypeScript错误:
元素隐式拥有'any'类型,因为字符串类型的表达式无法用于索引类型 '{ companyName: string; pocName: string; pocEmail: string; pocMobile: string; businessCard: { file: string; type: string; url: string; }; companyBaseAddress: { line1: string; line2: string; city: string; region: string; zip_code: string; country: string; }[]; }'。未找到接受string类型参数的索引签名。
原代码示例
const [basicDetails, setBasicDetails] = useState({ companyName: "", pocName: "", pocEmail: "", pocMobile: "", businessCard: { file: "", type: "", url: "" }, companyBaseAddress: [defaultAddress] }); const [errorMessage, setErrorMessage] = useState({ companyName: "", pocName: "", pocEmail: "", pocMobile: "", businessCard: { file: "", type: "", url: "" }, companyBaseAddress: [defaultAddress] }); const validation = () => { Object.keys(errorMessage).map((field => basicDetails[field] === "" ? setErrorMessage(prevState => ({ ...prevState, [field]: "Can not be Empty" })) : null)); };
问题原因
Object.keys()返回的是string[]类型,TypeScript无法确认这些字符串就是basicDetails和errorMessage的合法键名,因此索引时会抛出类型错误。另外原代码还存在逻辑问题:businessCard是对象、companyBaseAddress是数组,它们永远不会等于空字符串,直接判断会无效。
解决方法
方法1:定义类型别名并断言键类型
先为状态对象定义明确的类型,再将Object.keys()的结果断言为类型的键数组,同时增加类型判断避免无效验证:
// 定义基础详情类型 type BasicDetails = { companyName: string; pocName: string; pocEmail: string; pocMobile: string; businessCard: { file: string; type: string; url: string }; companyBaseAddress: { line1: string; line2: string; city: string; region: string; zip_code: string; country: string }[]; }; // 定义错误信息类型,与BasicDetails结构匹配 type ErrorMessage = { [K in keyof BasicDetails]: BasicDetails[K] extends string ? string : BasicDetails[K] extends object ? typeof BasicDetails['businessCard'] : typeof BasicDetails['companyBaseAddress']; }; // 初始化状态时指定类型 const [basicDetails, setBasicDetails] = useState<BasicDetails>({ companyName: "", pocName: "", pocEmail: "", pocMobile: "", businessCard: { file: "", type: "", url: "" }, companyBaseAddress: [defaultAddress] }); const [errorMessage, setErrorMessage] = useState<ErrorMessage>({ companyName: "", pocName: "", pocEmail: "", pocMobile: "", businessCard: { file: "", type: "", url: "" }, companyBaseAddress: [defaultAddress] }); const validation = () => { // 将field断言为BasicDetails的合法键 (Object.keys(errorMessage) as Array<keyof BasicDetails>).map(field => { // 仅验证字符串类型的字段 if (typeof basicDetails[field] === 'string' && basicDetails[field] === "") { setErrorMessage(prevState => ({ ...prevState, [field]: "Can not be Empty" })); } return null; }); };
方法2:手动指定需要验证的字段数组
直接创建包含合法键的数组,TypeScript能准确推断类型,同时避免遍历无关字段:
// 先定义BasicDetails类型(同方法1) type BasicDetails = { /* ... */ }; const [basicDetails, setBasicDetails] = useState<BasicDetails>({ /* ... */ }); const [errorMessage, setErrorMessage] = useState<ErrorMessage>({ /* ... */ }); const validation = () => { // 只列出需要验证的字符串类型字段 const fieldsToValidate: Array<keyof BasicDetails> = ['companyName', 'pocName', 'pocEmail', 'pocMobile']; fieldsToValidate.forEach(field => { if (basicDetails[field] === "") { setErrorMessage(prev => ({ ...prev, [field]: "Can not be Empty" })); } }); };
注意事项
- 不要给
BasicDetails添加[key: string]: any的索引签名,这会丢失TypeScript的类型检查能力。 - 必须区分字段类型,不要对非字符串类型的字段做空字符串判断,避免无效逻辑。
内容的提问来源于stack exchange,提问作者Shiva Giri

