TypeScript报错:类型无法赋值给never类型参数问题排查
我写了这段代码:
if (!target.unit.subTypes.includes(effectAmount.multiplicator.subType)) continue
触发了TypeScript错误:
TS2345: Argument of type 'CreatureSubType | StructureSubType' is not assignable to parameter of type 'never'. Type 'CreatureSubType' is not assignable to type 'never'.
相关类型定义如下:
target.unit.subTypes的类型:
public subTypes: CreatureSubType[] | StructureSubType[]
multiplicator.subType的类型:
subType?: CreatureSubType | StructureSubType
CreatureSubType和StructureSubType都是常规枚举。我已经处理了可选类型的情况,但搞不懂报错的原因。
后来我构建了最小复现示例:
export enum CreatureSubType { HUMAN = 1 } export enum StructureSubType { TOWER = 2 } export class Foo { constructor(public subTypes: CreatureSubType[] | StructureSubType[]) {} }
使用时:
const foo = new Foo([CreatureSubType.HUMAN]) foo.subTypes.push(3)
依然报错:Argument of type 'number' is not assignable to parameter of type 'never'。
进一步测试发现,像number[] | boolean[]这类基础类型数组的联合类型也会出现相同问题,但非数组的联合类型(比如number | boolean)却不会。这是什么原因?
TypeScript对数组联合类型的类型推导逻辑和非数组联合类型完全不同:
- 对于非数组联合类型(如
number | boolean),TypeScript会将其视为「可以是两种类型之一」,允许传入符合任一类型的值。 - 但对于数组联合类型(如
CreatureSubType[] | StructureSubType[]),TypeScript会取两种数组类型的方法签名交集。以includes方法为例:CreatureSubType[]的includes方法接受的参数是CreatureSubType | undefinedStructureSubType[]的includes方法接受的参数是StructureSubType | undefined- 这两个参数类型的交集是
never(因为两个枚举没有重叠的类型),所以最终includes的参数类型被推导为never,导致传入CreatureSubType | StructureSubType时报错。
同样的逻辑适用于push方法:number[]的push接受number,boolean[]的push接受boolean,二者交集是never,所以传入任何值都会触发never类型错误。
有几种可行的解决方式:
1. 类型收窄
通过类型判断把数组联合类型收窄为具体的数组类型,让TypeScript明确当前数组的类型:
// 假设可以通过unit的类型属性区分是Creature还是Structure if (target.unit.type === 'creature') { if (!target.unit.subTypes.includes(effectAmount.multiplicator.subType as CreatureSubType)) continue } else { if (!target.unit.subTypes.includes(effectAmount.multiplicator.subType as StructureSubType)) continue }
2. 类型断言
如果你能确定当前场景下subType的类型和数组的类型匹配,可以直接断言:
if (!target.unit.subTypes.includes(effectAmount.multiplicator.subType as never)) continue
或者更精准地断言数组类型:
if (!(target.unit.subTypes as CreatureSubType[]).includes(effectAmount.multiplicator.subType as CreatureSubType)) continue
3. 重构类型定义
把数组联合类型改成元素为联合类型的数组,即(CreatureSubType | StructureSubType)[],这样includes方法的参数类型就会是CreatureSubType | StructureSubType | undefined,完全符合需求:
public subTypes: (CreatureSubType | StructureSubType)[]
不过这种方式需要确认业务逻辑是否允许数组同时包含两种类型的元素。
内容的提问来源于stack exchange,提问作者BoA456

