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

TypeScript剩余参数场景下为何无法正确执行类型收窄?

问题原因

这个类型错误来自TypeScript对数组、元组类型的控制流分析局限性:

  • 你定义的剩余参数类型[Coords] | number[]是两个数组类型的联合,TS不会因为你判断了x[0]类型为number,就自动把整个x收窄为number[]类型。
  • TS对数组索引的类型收窄是局部的:typeof x[0] === "number"仅能让TS确定x[0]是number,不会推导x[1]、x[2]等其他索引位的类型,也不会排除x为[Coords]元组的可能。
  • 逻辑上[Coords]是长度为1的元组,x为该类型时x[1]应为undefined,但TS处理「固定长度元组 | 普通数组」的联合类型时,不会对元组越界访问做静态校验,所有索引位的元素类型都会被计算为两个数组成员的元素类型联合,也就是Coords | number,因此x[1]等位置会被判定为可能传入Coords数组类型,无法直接赋值给要求number类型的矩阵元素位,触发类型错误。
修复方案

你可以选择以下任意一种方式解决问题:

方案1:分支内类型断言(实现最简单)

在判断x[0]为number的分支内,直接将x断言为number[]类型,跳过TS的不完整推导即可:

if(typeof(x[0]) == "number") {
  const nums = x as number[];
  return [ // Transposed
    1    , 0    , 0    , 0    ,
    0    , 1    , 0    , 0    ,
    0    , 0    , 1    , 0    ,
    nums[0] , nums[1]??0 , nums[2]??0 , nums[3]??1 ,
  ]
}

方案2:函数重载(类型定义最严谨)

为函数添加重载签名,明确两种入参模式对应的类型,TS可以在实现中正确识别分支类型:

// 重载:传入单个Coords类型参数
export function translate(coords: Coords): Matrix;
// 重载:传入多个number类型参数
export function translate(...nums: number[]): Matrix;
// 实现签名
export function translate(...x:[Coords]|number[]):Matrix{
  if(typeof(x[0]) == "number") {
    const nums = x as number[];
    return [
      1    , 0    , 0    , 0    ,
      0    , 1    , 0    , 0    ,
      0    , 0    , 1    , 0    ,
      nums[0] , nums[1]??0 , nums[2]??0 , nums[3]??1 ,
    ]
  }
  else {
    const X = normalize(x[0])
    return [
      1    , 0    , 0    , 0    ,
      0    , 1    , 0    , 0    ,
      0    , 0    , 1    , 0    ,
      X[0] , X[1] , X[2] , X[3] ,
    ]
  }
}

方案3:自定义类型守卫(运行时安全性最高)

编写类型守卫函数校验数组所有元素都是number,在判断通过时将x的类型收窄为number[],还可以避免混传number和Coords导致的隐藏运行时问题:

function isNumberArray(arr: unknown[]): arr is number[] {
  return arr.every(item => typeof item === "number");
}

export function translate(...x:[Coords]|number[]):Matrix{
  if(isNumberArray(x)) {
    return [
      1    , 0    , 0    , 0    ,
      0    , 1    , 0    , 0    ,
      0    , 0    , 1    , 0    ,
      x[0] , x[1]??0 , x[2]??0 , x[3]??1 ,
    ]
  }
  else {
    const X = normalize(x[0])
    return [
      1    , 0    , 0    , 0    ,
      0    , 1    , 0    , 0    ,
      0    , 0    , 1    , 0    ,
      X[0] , X[1] , X[2] , X[3] ,
    ]
  }
}

内容的提问来源于stack exchange,提问作者hl037_

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 04:51:22