TypeScript:对象数组赋值对象键时遇'never'类型错误求助
TypeScript赋值报错:'string | number | boolean'无法赋值给'never'类型
错误信息
Type 'string | number | boolean' is not assignable to type 'never'.
初始代码
type FileObject = { name: string path: string returnStatus: number dataPrepared: boolean } type Keys = keyof FileObject type KeyValue = { [key in Keys]: {key: key, value: FileObject[key]} }[Keys] type Mutation = { index: number } & KeyValue const data: FileObject[] = [] function mutateData(input: Mutation) { data[input.index][input.key] = input.value }
我不明白报错原因,明明data已经被初始化为正确类型的空数组。在我看来这个方案应该可行,因为Mutation类型包含index以及正确的键/值对联合类型。
真实使用场景补充
以下是实际业务代码,我希望给switch语句使用单一类型,核心要求是避免使用泛型:
type FileObject = { name: string path: string returnStatus: number dataPrepared: boolean } type Keys = keyof FileObject type KeyValue = { [key in Keys]: {key: key, value: FileObject[key]} }[Keys] type Mutation = { switchType: 'singleValue' index: number } & KeyValue type ReturnObject = { switchType: 'returnObject' index: number } type DataHandler = Mutation | ReturnObject const data: FileObject[] = [] function dataHandler(input: DataHandler) { switch (input.switchType) { case 'singleValue': data[input.index][input.key] = input.value break case 'returnObject': console.log(data[input.index]) break default: return } }
问题根源
TypeScript在处理联合类型时,无法自动关联input.key和input.value的类型绑定。虽然Mutation是由各个键值对组成的联合类型,但TypeScript会把input.key解析为所有键的联合('name' | 'path' | 'returnStatus' | 'dataPrepared'),input.value解析为所有值类型的联合(string | number | boolean),两者之间的类型关联丢失了。此时TypeScript会认为目标属性的类型是never——因为没有任何类型能同时兼容string、number和boolean,所以报错。
无泛型解决方案
方案1:类型守卫拆分联合类型
通过自定义类型守卫函数,明确每个键对应的类型,让TypeScript能正确推断:
type FileObject = { name: string path: string returnStatus: number dataPrepared: boolean } type Keys = keyof FileObject type KeyValue = { [key in Keys]: {key: key, value: FileObject[key]} }[Keys] type Mutation = { switchType: 'singleValue' index: number } & KeyValue type ReturnObject = { switchType: 'returnObject' index: number } type DataHandler = Mutation | ReturnObject const data: FileObject[] = [] // 定义类型守卫函数 function isNameMut(mut: Mutation): mut is Mutation & {key: 'name', value: string} { return mut.key === 'name' } function isPathMut(mut: Mutation): mut is Mutation & {key: 'path', value: string} { return mut.key === 'path' } function isReturnStatusMut(mut: Mutation): mut is Mutation & {key: 'returnStatus', value: number} { return mut.key === 'returnStatus' } function isDataPreparedMut(mut: Mutation): mut is Mutation & {key: 'dataPrepared', value: boolean} { return mut.key === 'dataPrepared' } function dataHandler(input: DataHandler) { switch (input.switchType) { case 'singleValue': const item = data[input.index] if (!item) break // 处理数组越界情况 if (isNameMut(input)) { item.name = input.value } else if (isPathMut(input)) { item.path = input.value } else if (isReturnStatusMut(input)) { item.returnStatus = input.value } else if (isDataPreparedMut(input)) { item.dataPrepared = input.value } break case 'returnObject': console.log(data[input.index]) break default: return } }
方案2:类型断言简化实现
如果不想写多个守卫函数,可以用类型断言直接告知TypeScript键值类型匹配,适合类型数量较少的场景:
// 其他类型定义不变 function dataHandler(input: DataHandler) { switch (input.switchType) { case 'singleValue': const item = data[input.index] if (item) { // 断言目标属性类型与value一致 (item[input.key] as typeof input.value) = input.value } break case 'returnObject': console.log(data[input.index]) break default: return } }
方案3:映射对象处理赋值
用一个映射对象封装每个键的赋值逻辑,代码更简洁:
// 其他类型定义不变 function dataHandler(input: DataHandler) { switch (input.switchType) { case 'singleValue': const item = data[input.index] if (!item) break // 定义每个键对应的赋值函数 const setters = { name: (val: string) => item.name = val, path: (val: string) => item.path = val, returnStatus: (val: number) => item.returnStatus = val, dataPrepared: (val: boolean) => item.dataPrepared = val, } as const // 调用对应赋值函数,用never断言跳过类型检查(因为已经通过映射保证类型安全) setters[input.key](input.value as never) break case 'returnObject': console.log(data[input.index]) break default: return } }
内容的提问来源于stack exchange,提问作者MajkelTMB
相关产品推荐
相关产品推荐

