TypeScript缺少索引签名时为何会将类型推断为never?
类型错误原因解析
未给FormData接口添加索引签名时,动态属性赋值位置被推断为never,是TypeScript严格类型校验规则的正常表现,具体逻辑如下:
错误触发原理
- 无索引签名的前提下,
keyof FormData返回属性名的联合类型:"applicationName" | "cluster" | "stackCode" | "GitHubToken",四个属性对应的类型分别为string、string、number、string。 - 执行
nextState[action.field] = action.payload赋值时,TypeScript在编译阶段无法确定action.field的具体取值,也就无法锁定实际要赋值的目标属性:传入的字段可能是要求number类型的stackCode,也可能是要求string类型的其余三个字段。 - 为了保证所有赋值场景下的类型安全,TS会要求赋值内容必须同时满足所有可能目标属性的类型约束,也就是取所有属性类型的交叉类型:
string & string & number & string。不存在任何值能同时满足string和number的类型要求,因此这个交叉类型等价于never。 - 此时
action.payload!的类型是string | number,自然无法赋值给never类型,就会抛出对应的类型错误。
添加索引签名后错误消失的原因
新增的字符串索引签名[index: string]: boolean | number | string | string[],相当于给TS做了统一的类型约定:所有通过字符串键访问该接口得到的值,都属于这个联合类型的范围。
这种情况下,TS不会再对动态键对应的具体属性类型做交叉计算,直接将动态赋值的目标类型判定为索引签名声明的返回值类型,只要传入的payload符合该联合类型要求,就不会触发类型报错。
注意:这种写法本质是放宽了类型校验规则,并没有从根源解决类型匹配问题——如果后续给
stackCode字段传入字符串类型的payload,TS不会抛出任何错误,可能引发运行时异常。如果要实现严格类型安全,更推荐给FieldAction增加泛型约束,在构造action时就绑定字段和对应payload的类型映射关系,避免动态赋值的类型不确定性。
初始问题代码
interface FormData { applicationName: string; cluster: string; stackCode: number; GitHubToken: string; } enum FieldChangeType { TextInput, Toggle, } interface FieldAction { type: FieldChangeType; field: keyof FormData; payload?: FormData[keyof FormData]; } function useFormRedux() { function reducer(preState: FormData, action: FieldAction) { const nextState: FormData = cloneDeep(preState); switch(action.type) { case FieldChangeType.TextInput: nextState[action.field] = action.payload!; // Error: Type 'string | number' is not assignable to type 'never'. } return nextState; } }
添加索引签名后的修改代码
interface FormData { [index: string]: boolean | number | string | string[] applicationName: string; cluster: string; stackCode: number; GitHubToken: string; }
内容的提问来源于stack exchange,提问作者Pandy
相关产品推荐
相关产品推荐

