You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 15:37:05