动态设置多类型对象属性时出现Type 'string|number'不可赋值给'never'错误
解决TypeScript动态更新对象属性时的类型不匹配错误
问题描述
编写了一个用于更新对象属性的函数:先将输入字符串转换为对应标量类型,再根据传入的属性名动态赋值。迁移至TypeScript后,settings[setting] = value行出现以下类型错误:
Type 'string | number' is not assignable to type 'never'. Type 'string' is not assignable to type 'never'.(2322)
复现代码:
const defaultSettings = { intProp: 10, stringProp: 'abc', }; type Settings = typeof defaultSettings; const settings = defaultSettings; function updateSetting( setting: keyof typeof settings, str: string, type: 'integer' | 'string' ) { if (!(setting || setting in defaultSettings)) return; if (!str || str.length === 0) return; let value: typeof settings[keyof typeof settings]; if (type === 'integer') value = parseInt(str, 10); else value = str; // TS error here settings[setting] = value; } updateSetting('intProp', '20', 'integer'); console.log(settings);
错误原因
TypeScript无法推断出setting对应的属性类型与value的类型是严格匹配的:
setting是keyof Settings类型,可能是'intProp'或'stringProp'value是string | number的联合类型- TS会严格检查每个属性的类型约束:
intProp仅接受number,stringProp仅接受string。联合类型string | number无法满足单个属性的精确类型要求,因此报错。
解决方案
方案一:泛型+类型映射(推荐,兼顾类型安全与灵活性)
通过泛型关联属性名和对应的类型标记,让TypeScript能精确推断类型匹配关系:
const defaultSettings = { intProp: 10, stringProp: 'abc', }; type Settings = typeof defaultSettings; const settings = defaultSettings; // 建立属性名与类型标记的映射关系 type SettingTypeMap = { intProp: 'integer'; stringProp: 'string'; }; function updateSetting<K extends keyof Settings>( setting: K, str: string, type: SettingTypeMap[K] ) { if (!(setting in defaultSettings)) return; if (!str || str.length === 0) return; const value: Settings[K] = type === 'integer' ? parseInt(str, 10) as Settings[K] : str as Settings[K]; settings[setting] = value; } // 合法调用 updateSetting('intProp', '20', 'integer'); updateSetting('stringProp', 'def', 'string'); // 非法调用会被TS拦截(符合预期) // updateSetting('intProp', '20', 'string');
说明:
- 泛型
K约束为属性名,SettingTypeMap[K]确保传入的type是当前属性对应的合法类型标记 value被约束为当前属性的精确类型Settings[K],TS能确认赋值的类型安全性
方案二:类型守卫细化类型(适合属性较少的场景)
通过显式判断属性名和类型,让TS细化类型推断:
function updateSetting( setting: keyof Settings, str: string, type: 'integer' | 'string' ) { if (!(setting in defaultSettings)) return; if (!str || str.length === 0) return; if (setting === 'intProp' && type === 'integer') { settings[setting] = parseInt(str, 10); } else if (setting === 'stringProp' && type === 'string') { settings[setting] = str; } }
说明:
- 每个分支明确对应属性和类型的匹配关系,TS能准确检查类型
- 缺点是属性较多时代码会变得繁琐
方案三:类型断言(临时解决,不推荐)
如果不需要严格类型检查,可以用类型断言跳过TS的错误提示,但会丢失类型安全:
// 修改赋值行 settings[setting] = value as never;
说明:
- 这种方法会关闭TS的类型校验,可能导致错误赋值(比如给
intProp传入字符串),仅适合临时调试使用
内容的提问来源于stack exchange,提问作者Joost Schuur
相关产品推荐
相关产品推荐

